summer-engine 3.0.0 → 3.2.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 -2
- package/.claude-plugin/plugin.json +27 -5
- package/.codex-plugin/plugin.json +28 -6
- package/.cursor-plugin/plugin.json +27 -5
- package/.factory-plugin/plugin.json +27 -5
- package/.opencode/plugins/summer.js +1 -1
- package/CHANGELOG.md +120 -0
- package/README.md +22 -7
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/setup.js +6 -19
- package/dist/cli/commands/skills.js +57 -32
- package/dist/core/api-client.d.ts +9 -1
- package/dist/core/api-client.js +21 -4
- package/dist/core/capabilities/agent-playbook.js +46 -0
- package/dist/core/capabilities/audit/accept.d.ts +55 -0
- package/dist/core/capabilities/audit/accept.js +147 -0
- package/dist/core/capabilities/audit/args.d.ts +95 -0
- package/dist/core/capabilities/audit/args.js +89 -0
- package/dist/core/capabilities/audit/audit.d.ts +56 -0
- package/dist/core/capabilities/audit/audit.js +287 -0
- package/dist/core/capabilities/audit/frame.d.ts +14 -0
- package/dist/core/capabilities/audit/frame.js +35 -0
- package/dist/core/capabilities/audit/gaps.d.ts +96 -0
- package/dist/core/capabilities/audit/gaps.js +257 -0
- package/dist/core/capabilities/audit/judge.d.ts +162 -0
- package/dist/core/capabilities/audit/judge.js +1134 -0
- package/dist/core/capabilities/audit/math.d.ts +110 -0
- package/dist/core/capabilities/audit/math.js +263 -0
- package/dist/core/capabilities/audit/summary.d.ts +74 -0
- package/dist/core/capabilities/audit/summary.js +212 -0
- package/dist/core/capabilities/board.d.ts +31 -0
- package/dist/core/capabilities/board.js +43 -0
- package/dist/core/capabilities/camera-view.d.ts +4 -2
- package/dist/core/capabilities/camera-view.js +6 -3
- package/dist/core/capabilities/connect-signal.d.ts +85 -0
- package/dist/core/capabilities/connect-signal.js +285 -0
- package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
- package/dist/core/capabilities/engine-fallbacks.js +5 -0
- package/dist/core/capabilities/engine-ops.d.ts +5 -1
- package/dist/core/capabilities/engine-ops.js +0 -3
- package/dist/core/capabilities/engine-receipt.d.ts +12 -0
- package/dist/core/capabilities/engine-receipt.js +26 -0
- package/dist/core/capabilities/file-read.d.ts +64 -0
- package/dist/core/capabilities/file-read.js +325 -0
- package/dist/core/capabilities/game-task-plan.js +4 -2
- package/dist/core/capabilities/grep.d.ts +60 -0
- package/dist/core/capabilities/grep.js +190 -0
- package/dist/core/capabilities/inspect-node.d.ts +56 -0
- package/dist/core/capabilities/inspect-node.js +225 -0
- package/dist/core/capabilities/inspect-resource.d.ts +61 -0
- package/dist/core/capabilities/inspect-resource.js +376 -0
- package/dist/core/capabilities/math3d.d.ts +28 -0
- package/dist/core/capabilities/math3d.js +88 -0
- package/dist/core/capabilities/motion-generation.d.ts +62 -0
- package/dist/core/capabilities/motion-generation.js +138 -0
- package/dist/core/capabilities/placement-batch.d.ts +102 -0
- package/dist/core/capabilities/placement-batch.js +483 -0
- package/dist/core/capabilities/placement-math.d.ts +70 -0
- package/dist/core/capabilities/placement-math.js +209 -0
- package/dist/core/capabilities/placement-script.d.ts +42 -0
- package/dist/core/capabilities/placement-script.js +1096 -0
- package/dist/core/capabilities/placement.d.ts +485 -0
- package/dist/core/capabilities/placement.js +1731 -0
- package/dist/core/capabilities/project-context.d.ts +33 -2
- package/dist/core/capabilities/project-context.js +87 -4
- package/dist/core/capabilities/replace-node.d.ts +91 -0
- package/dist/core/capabilities/replace-node.js +423 -0
- package/dist/core/capabilities/runtime-control.d.ts +20 -4
- package/dist/core/capabilities/runtime-control.js +67 -2
- package/dist/core/capabilities/scene-batch.d.ts +63 -0
- package/dist/core/capabilities/scene-batch.js +394 -0
- package/dist/core/capabilities/scene-readback.d.ts +36 -0
- package/dist/core/capabilities/scene-readback.js +54 -0
- package/dist/core/capabilities/seeing/args.d.ts +348 -0
- package/dist/core/capabilities/seeing/args.js +178 -0
- package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
- package/dist/core/capabilities/seeing/candidates.js +282 -0
- package/dist/core/capabilities/seeing/math.d.ts +134 -0
- package/dist/core/capabilities/seeing/math.js +269 -0
- package/dist/core/capabilities/seeing/probe.d.ts +62 -0
- package/dist/core/capabilities/seeing/probe.js +188 -0
- package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
- package/dist/core/capabilities/seeing/scoring.js +639 -0
- package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
- package/dist/core/capabilities/seeing/seeing.js +1341 -0
- package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
- package/dist/core/capabilities/seeing/shot-store.js +204 -0
- package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
- package/dist/core/capabilities/surface-snap-mesh.js +425 -0
- package/dist/core/capabilities/surface-snap.d.ts +77 -0
- package/dist/core/capabilities/surface-snap.js +461 -0
- package/dist/core/capabilities/tool-dispatch.js +231 -72
- package/dist/core/capabilities/tscn.d.ts +85 -0
- package/dist/core/capabilities/tscn.js +391 -0
- package/dist/core/capabilities/world-snapshot.d.ts +58 -0
- package/dist/core/capabilities/world-snapshot.js +150 -0
- package/dist/core/capability-skew.d.ts +5 -5
- package/dist/core/capability-skew.js +14 -6
- package/dist/core/headless/worker-engine-client.d.ts +1 -1
- package/dist/core/headless/worker-engine-client.js +2 -2
- package/dist/core/library-read.d.ts +37 -5
- package/dist/core/library-read.js +264 -33
- package/dist/core/library-search.d.ts +2 -2
- package/dist/core/skills-registry.d.ts +9 -1
- package/dist/core/skills-registry.js +25 -2
- package/dist/installer/agent-config.d.ts +9 -4
- package/dist/installer/agent-config.js +194 -392
- package/dist/installer/agent-table.d.ts +119 -0
- package/dist/installer/agent-table.js +619 -0
- package/dist/installer/setup.js +4 -2
- package/dist/installer/skill-locations.d.ts +8 -17
- package/dist/installer/skill-locations.js +22 -101
- package/dist/installer/version-check.d.ts +5 -1
- package/dist/installer/version-check.js +38 -28
- package/dist/mcp/server.js +9 -3
- package/dist/mcp/tools/asset-tools.js +21 -3
- package/dist/mcp/tools/audit-tools.d.ts +23 -0
- package/dist/mcp/tools/audit-tools.js +60 -0
- package/dist/mcp/tools/debug-tools.js +3 -1
- package/dist/mcp/tools/file-tools.js +17 -4
- package/dist/mcp/tools/generate-tools.js +74 -66
- package/dist/mcp/tools/library-tools.js +2 -0
- package/dist/mcp/tools/perception-tools.js +6 -19
- package/dist/mcp/tools/placement-tools.d.ts +24 -0
- package/dist/mcp/tools/placement-tools.js +91 -0
- package/dist/mcp/tools/project-tools.js +18 -15
- package/dist/mcp/tools/scene-tools.js +71 -83
- package/dist/mcp/tools/seeing-tools.d.ts +24 -0
- package/dist/mcp/tools/seeing-tools.js +64 -0
- package/dist/mcp/tools/spatial-tools.js +17 -9
- package/dist/mcp/tools/visual-tools.js +59 -7
- package/gemini-extension.json +26 -4
- package/integrations/README.md +48 -19
- package/integrations/amp/README.md +12 -0
- package/integrations/amp/manifest-target.json +3 -0
- package/integrations/antigravity/README.md +12 -0
- package/integrations/antigravity/manifest-target.json +3 -0
- package/integrations/claude-desktop/README.md +12 -0
- package/integrations/claude-desktop/manifest-target.json +3 -0
- package/integrations/cline/README.md +8 -9
- package/integrations/cline-cli/README.md +12 -0
- package/integrations/cline-cli/manifest-target.json +3 -0
- package/integrations/copilot-jetbrains/README.md +12 -0
- package/integrations/copilot-jetbrains/manifest-target.json +3 -0
- package/integrations/crush/README.md +12 -0
- package/integrations/crush/manifest-target.json +3 -0
- package/integrations/github-copilot/README.md +8 -8
- package/integrations/goose/README.md +12 -0
- package/integrations/goose/manifest-target.json +3 -0
- package/integrations/grok-build/README.md +12 -0
- package/integrations/grok-build/manifest-target.json +3 -0
- package/integrations/hermes/README.md +12 -0
- package/integrations/hermes/manifest-target.json +3 -0
- package/integrations/junie/README.md +12 -0
- package/integrations/junie/manifest-target.json +3 -0
- package/integrations/kilo-code/README.md +6 -10
- package/integrations/kimi-code/README.md +12 -0
- package/integrations/kimi-code/manifest-target.json +3 -0
- package/integrations/kiro/README.md +12 -0
- package/integrations/kiro/manifest-target.json +3 -0
- package/integrations/lm-studio/README.md +8 -8
- package/integrations/mistral-vibe/README.md +12 -0
- package/integrations/mistral-vibe/manifest-target.json +3 -0
- package/integrations/opencode/README.md +9 -12
- package/integrations/qoder/README.md +12 -0
- package/integrations/qoder/manifest-target.json +3 -0
- package/integrations/qwen-code/README.md +12 -0
- package/integrations/qwen-code/manifest-target.json +3 -0
- package/integrations/roo-code/README.md +8 -7
- package/integrations/rovo-dev/README.md +12 -0
- package/integrations/rovo-dev/manifest-target.json +3 -0
- package/integrations/trae/README.md +12 -0
- package/integrations/trae/manifest-target.json +3 -0
- package/integrations/visual-studio/README.md +12 -0
- package/integrations/visual-studio/manifest-target.json +3 -0
- package/integrations/vscode-copilot/README.md +9 -9
- package/integrations/warp/README.md +12 -0
- package/integrations/warp/manifest-target.json +3 -0
- package/integrations/windsurf/README.md +8 -9
- package/integrations/zed/README.md +12 -0
- package/integrations/zed/manifest-target.json +3 -0
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/summer-folder/resource.yaml +1 -1
- package/library/references/summer-folder/summer-folder.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +1 -1
- package/library/skills/README.md +264 -0
- package/library/skills/adaptive-music/SKILL.md +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +220 -0
- package/library/skills/ai-characters-in-games/resource.yaml +30 -0
- package/library/skills/ambient-bed/SKILL.md +1 -1
- package/library/skills/animated-loop/SKILL.md +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/art-direction/SKILL.md +1 -1
- package/library/skills/asset-strategy/SKILL.md +2 -1
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/browse-templates/SKILL.md +3 -3
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +128 -0
- package/library/skills/camera-rigs/resource.yaml +29 -0
- package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
- package/library/skills/character-animation-wiring/SKILL.md +1 -1
- package/library/skills/character-model/SKILL.md +1 -1
- package/library/skills/character-movement/SKILL.md +326 -0
- package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
- package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
- package/library/skills/character-movement/references/movement-3d.md +136 -0
- package/library/skills/character-movement/references/platformer-feel.md +83 -0
- package/library/skills/character-movement/resource.yaml +40 -0
- package/library/skills/character-portrait/SKILL.md +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +1 -1
- package/library/skills/combat-basics/SKILL.md +159 -0
- package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
- package/library/skills/combat-basics/resource.yaml +34 -0
- package/library/skills/concept-art/SKILL.md +1 -1
- package/library/skills/create-asset-sheet/SKILL.md +1 -1
- package/library/skills/debug/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/design-level/SKILL.md +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-npc/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/environment-kit/SKILL.md +1 -1
- package/library/skills/export-and-ship/SKILL.md +1 -1
- package/library/skills/fabricating-assets/SKILL.md +1 -1
- package/library/skills/facial-and-lipsync/SKILL.md +1 -1
- package/library/skills/fps-controller/SKILL.md +9 -7
- package/library/skills/fps-controller/resource.yaml +4 -1
- package/library/skills/game-feel/SKILL.md +1 -1
- package/library/skills/gameskill/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/generate-motion/SKILL.md +9 -9
- package/library/skills/generate-motion/resource.yaml +2 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/in-game-purchases/SKILL.md +254 -0
- package/library/skills/in-game-purchases/resource.yaml +29 -0
- package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
- package/library/skills/investigating-bugs/SKILL.md +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
- package/library/skills/make-game/SKILL.md +25 -3
- package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
- package/library/skills/make-game/references/2d-platformer.md +117 -0
- package/library/skills/make-game/references/2d-rpg-content.md +234 -0
- package/library/skills/make-game/references/2d-rpg.md +241 -0
- package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
- package/library/skills/make-game/references/2d-survivors.md +237 -0
- package/library/skills/make-game/references/3d-fps-systems.md +216 -0
- package/library/skills/make-game/references/3d-fps.md +177 -0
- package/library/skills/make-game/references/3d-horror-systems.md +186 -0
- package/library/skills/make-game/references/3d-horror.md +201 -0
- package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
- package/library/skills/make-game/references/3d-third-person.md +215 -0
- package/library/skills/make-game/resource.yaml +6 -1
- package/library/skills/multiplayer/SKILL.md +150 -0
- package/library/skills/multiplayer/resource.yaml +33 -0
- package/library/skills/multiplayer-movement/SKILL.md +510 -0
- package/library/skills/multiplayer-movement/files.md +501 -0
- package/library/skills/multiplayer-movement/resource.yaml +30 -0
- package/library/skills/multiplayer-project/SKILL.md +521 -0
- package/library/skills/multiplayer-project/files.md +448 -0
- package/library/skills/multiplayer-project/resource.yaml +36 -0
- package/library/skills/multiplayer-publish/SKILL.md +229 -0
- package/library/skills/multiplayer-publish/export-errors.md +89 -0
- package/library/skills/multiplayer-publish/resource.yaml +28 -0
- package/library/skills/multiplayer-state/SKILL.md +438 -0
- package/library/skills/multiplayer-state/files.md +592 -0
- package/library/skills/multiplayer-state/resource.yaml +35 -0
- package/library/skills/multiplayer-testing/SKILL.md +236 -0
- package/library/skills/multiplayer-testing/resource.yaml +39 -0
- package/library/skills/music-track/SKILL.md +1 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/organic-model/SKILL.md +1 -1
- package/library/skills/pixel-art/SKILL.md +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/procedural-animation/SKILL.md +2 -2
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +1 -1
- package/library/skills/psx-retro-rendering/SKILL.md +1 -1
- package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/retarget/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/save-load/SKILL.md +156 -0
- package/library/skills/save-load/resource.yaml +27 -0
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
- package/library/skills/scene-scripting/SKILL.md +1 -1
- package/library/skills/scene-to-level/SKILL.md +1 -1
- package/library/skills/skill-create/SKILL.md +1 -1
- package/library/skills/skill-improve/SKILL.md +1 -1
- package/library/skills/skill-test/SKILL.md +1 -1
- package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
- package/library/skills/skybox-panorama/SKILL.md +1 -1
- package/library/skills/sound-effect/SKILL.md +1 -1
- package/library/skills/spatial-placement/SKILL.md +134 -3
- package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
- package/library/skills/spatial-placement/resource.yaml +16 -2
- package/library/skills/sprite-sheet/SKILL.md +1 -1
- package/library/skills/summer-analytics/SKILL.md +147 -0
- package/library/skills/summer-analytics/resource.yaml +28 -0
- package/library/skills/summer-friends/SKILL.md +226 -0
- package/library/skills/summer-friends/resource.yaml +28 -0
- package/library/skills/summer-leaderboards/SKILL.md +231 -0
- package/library/skills/summer-leaderboards/resource.yaml +30 -0
- package/library/skills/summer-match-results/SKILL.md +286 -0
- package/library/skills/summer-match-results/resource.yaml +32 -0
- package/library/skills/summer-matchmaking/SKILL.md +301 -0
- package/library/skills/summer-matchmaking/resource.yaml +32 -0
- package/library/skills/summer-parties/SKILL.md +253 -0
- package/library/skills/summer-parties/resource.yaml +30 -0
- package/library/skills/summer-player-data/SKILL.md +322 -0
- package/library/skills/summer-player-data/resource.yaml +30 -0
- package/library/skills/summer-store/SKILL.md +271 -0
- package/library/skills/summer-store/resource.yaml +29 -0
- package/library/skills/summer-world-chat/SKILL.md +195 -0
- package/library/skills/summer-world-chat/resource.yaml +30 -0
- package/library/skills/summer-world-saves/SKILL.md +284 -0
- package/library/skills/summer-world-saves/resource.yaml +29 -0
- package/library/skills/text-to-motion/SKILL.md +99 -0
- package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
- package/library/skills/text-to-motion/resource.yaml +41 -0
- package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
- package/library/skills/tileable-texture/SKILL.md +1 -1
- package/library/skills/trailer-shot/SKILL.md +1 -1
- package/library/skills/tune-performance/SKILL.md +1 -1
- package/library/skills/ui-basics/SKILL.md +10 -1
- package/library/skills/ui-graphics/SKILL.md +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/using-summer/SKILL.md +2 -2
- package/library/skills/using-summer/resource.yaml +2 -2
- package/library/skills/vehicle-model/SKILL.md +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verifying-scenes/SKILL.md +76 -2
- package/library/skills/verifying-scenes/resource.yaml +13 -2
- package/library/skills/vfx-dissolve/SKILL.md +1 -1
- package/library/skills/vfx-fire/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-lightning/SKILL.md +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
- package/library/skills/vfx-smoke/SKILL.md +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +1 -1
- package/library/skills/voice-line/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +38 -0
- package/library/skills/web-and-touch-input/resource.yaml +28 -0
- package/library/skills/web-compatibility-look/SKILL.md +62 -0
- package/library/skills/web-compatibility-look/resource.yaml +30 -0
- package/library/skills/world-building-3d/SKILL.md +8 -1
- package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +2 -1
- package/library/skills/writing-plans/SKILL.md +1 -1
- package/library/skills/writing-skills/SKILL.md +1 -1
- package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
- package/library/tools/attach-to-surface/resource.yaml +142 -0
- package/library/tools/batch/resource.yaml +9 -3
- package/library/tools/connect-ports/resource.yaml +120 -0
- package/library/tools/connect-signal/resource.yaml +3 -2
- package/library/tools/debug-views/resource.yaml +94 -0
- package/library/tools/frame-nodes/resource.yaml +104 -0
- package/library/tools/frame-shot/resource.yaml +134 -0
- package/library/tools/generate-motion/resource.yaml +44 -9
- package/library/tools/get-board/resource.yaml +50 -0
- package/library/tools/get-project-context/resource.yaml +13 -2
- package/library/tools/grep/resource.yaml +77 -0
- package/library/tools/inspect-asset/resource.yaml +71 -0
- package/library/tools/inspect-node/resource.yaml +9 -2
- package/library/tools/inspect-resource/resource.yaml +16 -5
- package/library/tools/instantiate-scene/resource.yaml +27 -2
- package/library/tools/measure/resource.yaml +96 -0
- package/library/tools/place-adjacent/resource.yaml +113 -0
- package/library/tools/play/resource.yaml +15 -2
- package/library/tools/raycast/resource.yaml +101 -0
- package/library/tools/read-file/resource.yaml +36 -2
- package/library/tools/read-library/resource.yaml +2 -2
- package/library/tools/repeat-along/resource.yaml +129 -0
- package/library/tools/replace-node/resource.yaml +11 -5
- package/library/tools/scene-audit/resource.yaml +144 -0
- package/library/tools/screenshot/resource.yaml +13 -2
- package/library/tools/search-assets/resource.yaml +18 -0
- package/library/tools/shot-sheet/resource.yaml +110 -0
- package/library/tools/snap-to-surface/resource.yaml +4 -1
- package/library/tools/world-snapshot/resource.yaml +24 -3
- package/library/tools/zoom/resource.yaml +105 -0
- package/package.json +3 -3
- package/registry/generated/aliases.json +17 -3
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +26 -4
- package/registry/generated/index.json +2154 -469
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +27 -5
- package/registry/generated/plugin.codex.json +28 -6
- package/registry/generated/plugin.cursor.json +27 -5
- package/registry/generated/plugin.factory.json +27 -5
- package/registry/generated/skills-index.md +264 -0
- package/registry/generated/skills-registry.json +901 -206
- package/registry/generated/templates-registry.json +2 -2
- package/library/skills/host-authoritative-state/SKILL.md +0 -332
- package/library/skills/host-authoritative-state/resource.yaml +0 -28
- package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
- package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
- package/library/skills/setup-multiplayer/SKILL.md +0 -336
- package/library/skills/setup-multiplayer/resource.yaml +0 -28
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# 2D Platformer - Enemies, Hazards, Juice, Progression
|
|
2
|
+
|
|
3
|
+
Add these only after the core loop (run/jump/goal/death) from `2d-platformer.md`
|
|
4
|
+
feels good. Combat mechanics reuse the `combat-basics` skill.
|
|
5
|
+
|
|
6
|
+
## Patrol enemy
|
|
7
|
+
|
|
8
|
+
A `CharacterBody2D` that walks, turns at ledges/walls, and hurts on contact. Ledge
|
|
9
|
+
detection via a downward `RayCast2D` at the front foot.
|
|
10
|
+
|
|
11
|
+
```gdscript
|
|
12
|
+
extends CharacterBody2D
|
|
13
|
+
@export var speed: float = 60.0
|
|
14
|
+
var dir: float = 1.0
|
|
15
|
+
var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
|
|
16
|
+
@onready var ledge: RayCast2D = $LedgeCheck # points down, ahead of feet
|
|
17
|
+
|
|
18
|
+
func _physics_process(delta: float) -> void:
|
|
19
|
+
if not is_on_floor():
|
|
20
|
+
velocity.y += gravity * delta
|
|
21
|
+
if is_on_wall() or (is_on_floor() and not ledge.is_colliding()):
|
|
22
|
+
dir *= -1.0
|
|
23
|
+
ledge.position.x *= -1.0
|
|
24
|
+
$Sprite2D.flip_h = dir < 0.0
|
|
25
|
+
velocity.x = dir * speed
|
|
26
|
+
move_and_slide()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Give it a hurtbox (masks PlayerHurtbox) and a Health node from `combat-basics`.
|
|
30
|
+
|
|
31
|
+
## Stomp-to-kill
|
|
32
|
+
|
|
33
|
+
The classic platformer verb. A separate Area2D under the player masking only the
|
|
34
|
+
Enemies layer; on overlap, kill the enemy and bounce the player.
|
|
35
|
+
|
|
36
|
+
```gdscript
|
|
37
|
+
# in player, StompArea (Area2D) child
|
|
38
|
+
func _on_stomp_body_entered(enemy: Node) -> void:
|
|
39
|
+
if velocity.y > 0.0 and enemy.has_method("die"):
|
|
40
|
+
enemy.die()
|
|
41
|
+
velocity.y = jump_velocity * 0.7 # bounce
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Falling (`velocity.y > 0`) distinguishes a stomp from a side hit that should damage
|
|
45
|
+
the player instead.
|
|
46
|
+
|
|
47
|
+
## Hazards
|
|
48
|
+
|
|
49
|
+
- Spikes/lava: an Area2D on the Triggers layer that calls `player.die()`.
|
|
50
|
+
- Bounce pad: Area2D that sets `player.velocity.y = -launch_force` on enter.
|
|
51
|
+
- Bottomless pit: one wide killzone under the level.
|
|
52
|
+
|
|
53
|
+
## Juice (the difference between prototype and game)
|
|
54
|
+
|
|
55
|
+
Route through a small helper so every hit feels alive:
|
|
56
|
+
|
|
57
|
+
- Hit flash: `modulate` to white, tween back over 0.06s.
|
|
58
|
+
- Screen shake: offset the Camera2D by decaying random noise for ~0.2s.
|
|
59
|
+
- Hitstop: `Engine.time_scale = 0.05` for one frame on a kill, then back to 1.0.
|
|
60
|
+
- Floating text: spawn a Label that rises and fades on coin/hit.
|
|
61
|
+
- Squash/stretch: tween the sprite `scale` to (1.2, 0.8) on land, back over 0.1s.
|
|
62
|
+
- Dust particles on land and on turn.
|
|
63
|
+
|
|
64
|
+
A silent, still platformer fails the 30-second bar even if every system works.
|
|
65
|
+
|
|
66
|
+
## Progression: coins, gems, and a level goal
|
|
67
|
+
|
|
68
|
+
- Coins increment `GameManager.score`; emit `score_changed` for the HUD.
|
|
69
|
+
- A `level_end` Area2D (flag/door) triggers the level-complete screen with time and
|
|
70
|
+
coin count, then advances to the next room `.tscn`.
|
|
71
|
+
- Save unlocked rooms + best times via the `save-load` skill.
|
|
72
|
+
|
|
73
|
+
## Metroidvania gating (optional)
|
|
74
|
+
|
|
75
|
+
For an interconnected map with ability gates:
|
|
76
|
+
|
|
77
|
+
- `AbilityManager` autoload: `unlocked: Dictionary`, `has(name)`, `unlock(name)`.
|
|
78
|
+
- Ability pickup: Area2D that calls `AbilityManager.unlock("double_jump")`.
|
|
79
|
+
- Ability gate: a wall/door that only opens if `AbilityManager.has("dash")`, or a
|
|
80
|
+
ledge only reachable with double jump.
|
|
81
|
+
- The player controller checks `AbilityManager.has("double_jump")` before allowing
|
|
82
|
+
a second air jump, `has("dash")` before a dash, etc.
|
|
83
|
+
|
|
84
|
+
Keep gating data-driven: one place lists what each ability unlocks, so designers
|
|
85
|
+
tune it without touching the controller.
|
|
86
|
+
|
|
87
|
+
## Traps
|
|
88
|
+
|
|
89
|
+
- Enemies as RigidBody2D fight the player physically; use CharacterBody2D or Area2D.
|
|
90
|
+
- Stomp check without a `velocity.y > 0` guard kills enemies on any touch.
|
|
91
|
+
- Ledge raycast that does not flip with direction walks enemies off cliffs.
|
|
92
|
+
- Piling on juice at once (shake + hitstop + flash on every coin) reads as noise;
|
|
93
|
+
reserve the big feedback for kills and damage, keep coins light.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# 2D Platformer - Core
|
|
2
|
+
|
|
3
|
+
Side-scrolling platformer blueprint: architecture, the level, and the core loop.
|
|
4
|
+
Movement itself lives in the `character-movement` skill (base controller) and its
|
|
5
|
+
`../../character-movement/references/platformer-feel.md` (coyote time, jump buffer, variable height) - do
|
|
6
|
+
not rewrite it here; wire it in. For Celeste-grade precision movement (dash, wall
|
|
7
|
+
jump, climb) use `celeste-momentum-platforming` instead.
|
|
8
|
+
|
|
9
|
+
## Minimum playable base (build this first)
|
|
10
|
+
|
|
11
|
+
Scene first (the make-game Scaffold phase): `main.tscn` with a Node2D root, a ground `StaticBody2D`
|
|
12
|
+
with a `CollisionShape2D`, the player, and a `Camera2D` child of the player. Prove
|
|
13
|
+
run + jump + land before anything else. Placeholder art is a colored
|
|
14
|
+
`ColorRect`/`Sprite2D`.
|
|
15
|
+
|
|
16
|
+
## Architecture (grows into a full game)
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
res://
|
|
20
|
+
scenes/
|
|
21
|
+
player/player.tscn
|
|
22
|
+
rooms/room_01.tscn # one .tscn per room/level
|
|
23
|
+
enemies/patrol_enemy.tscn
|
|
24
|
+
ui/{hud,pause_menu,main_menu,game_over}.tscn
|
|
25
|
+
scripts/
|
|
26
|
+
autoloads/
|
|
27
|
+
event_bus.gd # global signals, decouples systems
|
|
28
|
+
game_manager.gd # score, lives, current room, checkpoint
|
|
29
|
+
audio_manager.gd # pooled SFX + music
|
|
30
|
+
save_manager.gd
|
|
31
|
+
player/player_controller.gd
|
|
32
|
+
systems/{coin,checkpoint,killzone,moving_platform,one_way_platform,
|
|
33
|
+
crumbling_platform,bounce_pad,door,level_end}.gd
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Add autoloads only once the base loop works. Register in Project Settings in
|
|
37
|
+
dependency order: EventBus first (others reference it), then GameManager, then the
|
|
38
|
+
rest. All autoloads `process_mode = ALWAYS` so they survive pause.
|
|
39
|
+
|
|
40
|
+
## Collision layers (set names in Project Settings)
|
|
41
|
+
|
|
42
|
+
| Layer | Name | Used by |
|
|
43
|
+
|-------|------|---------|
|
|
44
|
+
| 1 | World | TileMap, static platforms |
|
|
45
|
+
| 2 | Player | Player body |
|
|
46
|
+
| 3 | Enemies | Enemy bodies |
|
|
47
|
+
| 4 | PlayerHitbox | Sword/stomp hit area |
|
|
48
|
+
| 5 | EnemyHitbox | Enemy hit area |
|
|
49
|
+
| 6 | PlayerHurtbox | Player hurt area |
|
|
50
|
+
| 7 | EnemyHurtbox | Enemy hurt area |
|
|
51
|
+
| 8 | Collectibles | Coins, gems |
|
|
52
|
+
| 9 | Triggers | Doors, killzones, checkpoints |
|
|
53
|
+
|
|
54
|
+
Hitboxes mask the OPPOSING hurtbox (see the `combat-basics` skill). The stomp detector is a
|
|
55
|
+
separate Area under the player masking only the Enemies layer.
|
|
56
|
+
|
|
57
|
+
## Building the level with a TileMap
|
|
58
|
+
|
|
59
|
+
Use a `TileMapLayer` (Godot 4.3+) for the solid world. Create a `TileSet`, paint
|
|
60
|
+
the collision polygon on the solid tiles, put the layer on the World collision
|
|
61
|
+
layer. The player collides with it for free - no per-tile StaticBody.
|
|
62
|
+
|
|
63
|
+
- One `TileMapLayer` for collidable terrain, a second (no collision, lower
|
|
64
|
+
z-index) for background decoration.
|
|
65
|
+
- For large levels, split into room `.tscn`s and load the current room via a
|
|
66
|
+
RoomManager; connect doors to `change_scene_to_file` or swap room instances.
|
|
67
|
+
- Parallax background: `Parallax2D` (or ParallaxBackground) layers with
|
|
68
|
+
`motion_scale` < 1 so distant layers move slower.
|
|
69
|
+
|
|
70
|
+
## Moving, one-way, and crumbling platforms
|
|
71
|
+
|
|
72
|
+
These three cover most platformer level tech.
|
|
73
|
+
|
|
74
|
+
```gdscript
|
|
75
|
+
# moving_platform.gd - AnimatableBody2D so riders move with it
|
|
76
|
+
extends AnimatableBody2D
|
|
77
|
+
@export var offset: Vector2 = Vector2(200, 0)
|
|
78
|
+
@export var duration: float = 2.0
|
|
79
|
+
func _ready() -> void:
|
|
80
|
+
var t := create_tween().set_loops().set_trans(Tween.TRANS_SINE)
|
|
81
|
+
t.tween_property(self, "position", position + offset, duration)
|
|
82
|
+
t.tween_property(self, "position", position, duration)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Use `AnimatableBody2D` (not StaticBody2D) for movers - it carries the player. For
|
|
86
|
+
one-way platforms, enable `one_way_collision` on the platform's CollisionShape2D
|
|
87
|
+
so the player jumps up through and lands on top. Crumbling platform: on
|
|
88
|
+
`body_entered`, start a short timer, shake, then disable collision and fade out;
|
|
89
|
+
re-enable after a respawn delay.
|
|
90
|
+
|
|
91
|
+
## Checkpoints and death
|
|
92
|
+
|
|
93
|
+
```gdscript
|
|
94
|
+
# checkpoint.gd
|
|
95
|
+
extends Area2D
|
|
96
|
+
func _ready() -> void:
|
|
97
|
+
body_entered.connect(func(b):
|
|
98
|
+
if b.is_in_group("player"):
|
|
99
|
+
GameManager.checkpoint = global_position)
|
|
100
|
+
|
|
101
|
+
# killzone.gd - fall/spike death
|
|
102
|
+
extends Area2D
|
|
103
|
+
func _ready() -> void:
|
|
104
|
+
body_entered.connect(func(b):
|
|
105
|
+
if b.is_in_group("player"):
|
|
106
|
+
b.die()) # player respawns at GameManager.checkpoint
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Core loop checklist (the 30-second bar)
|
|
110
|
+
|
|
111
|
+
- Run + jump feels good (tuned gravity/fall multiplier, coyote + buffer).
|
|
112
|
+
- Coins/goal give immediate feedback: sound + pop tween + counter tick.
|
|
113
|
+
- Death is instant and respawns at the last checkpoint, no long reload.
|
|
114
|
+
- A visible goal (flag/door) ends the level with a clear result.
|
|
115
|
+
|
|
116
|
+
Enemies, hazards, juice, and metroidvania progression are in
|
|
117
|
+
`2d-platformer-polish.md`.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# 2D Action RPG - Content Systems
|
|
2
|
+
|
|
3
|
+
The content layer on top of `2d-rpg.md`: enemies, NPCs and dialogue, quests,
|
|
4
|
+
inventory, loot, perks, status effects, and save/load. Everything here talks
|
|
5
|
+
to the rest of the game only through EventBus and the autoloads.
|
|
6
|
+
|
|
7
|
+
## Enemies (data-driven, five archetypes)
|
|
8
|
+
|
|
9
|
+
EnemyData resource defines stats + behavior; enemy_base.gd implements all
|
|
10
|
+
five behaviors and reads the data. No per-enemy scenes.
|
|
11
|
+
|
|
12
|
+
```gdscript
|
|
13
|
+
enum BehaviorType { CHASE, RANGED, SWARM, TANK, BOSS }
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- CHASE (slime): direct pursuit, melee lunge, low knockback resistance.
|
|
17
|
+
- RANGED (skeleton archer): holds preferred distance, fires projectiles,
|
|
18
|
+
strafes at range, backs off when crowded.
|
|
19
|
+
- SWARM (bat): fast, random jitter movement, contact damage.
|
|
20
|
+
- TANK (golem): slow, high HP, knockback_resistance 0.8, ground slam with
|
|
21
|
+
screen shake.
|
|
22
|
+
- BOSS (dark mage): cycling pattern (single shot -> fan of 3 -> charge),
|
|
23
|
+
phase 2 at 40% HP (faster everything + aura), can summon minions. Emits
|
|
24
|
+
`boss_health_changed` for the HUD boss bar.
|
|
25
|
+
|
|
26
|
+
EnemyData fields that matter for tuning: max_health, contact_damage,
|
|
27
|
+
move_speed, detection_range 200, attack_range 35, attack_cooldown 1.2,
|
|
28
|
+
knockback_resistance, xp_value, gold_min/max.
|
|
29
|
+
|
|
30
|
+
EnemyDatabase registers everything in code (no .tres files):
|
|
31
|
+
`db.get_enemy("slime")`, `db.get_enemies_for_wave(5)` returns a
|
|
32
|
+
wave-appropriate mix. Built-ins: slime, skeleton_archer, bat, golem,
|
|
33
|
+
dark_mage.
|
|
34
|
+
|
|
35
|
+
Contract: every enemy is in the "enemies" group and exposes
|
|
36
|
+
`take_hit(damage: int, source_position: Vector2, is_crit: bool = false)`.
|
|
37
|
+
Weapons and projectiles call it directly. Death drops XP + gold orbs via
|
|
38
|
+
`_drop_loot()` and emits `EventBus.enemy_killed(enemy, xp_value)`.
|
|
39
|
+
|
|
40
|
+
EnemySpawner is wave-based: `wave_interval` 12s, `enemies_per_wave_base` 3,
|
|
41
|
+
`enemies_per_wave_growth` 1, `max_enemies` 20, `difficulty_scale` 1.08
|
|
42
|
+
(HP/damage multiplier per wave). `spawn_boss("dark_mage", pos)` for arenas.
|
|
43
|
+
|
|
44
|
+
## NPCs and dialogue
|
|
45
|
+
|
|
46
|
+
DialogueData is a resource with a speaker name and a lines array. Each line
|
|
47
|
+
is a dictionary supporting optional keys:
|
|
48
|
+
|
|
49
|
+
```gdscript
|
|
50
|
+
{
|
|
51
|
+
"text": "Hello!",
|
|
52
|
+
"speaker": "Elder", # override
|
|
53
|
+
"choices": [{"text": "Yes", "next": 2}], # branching
|
|
54
|
+
"action": "start_quest:slay_monsters",
|
|
55
|
+
"condition": "quest_complete:slay_monsters"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Actions: `start_quest:<id>`, `give_item:<resource_path>`, `give_gold:<n>`,
|
|
60
|
+
`heal`. Conditions: `quest_complete:<id>`, `quest_active:<id>`,
|
|
61
|
+
`has_item:<id>`. This tiny string DSL covers most RPG dialogue needs -
|
|
62
|
+
resist building a graph editor.
|
|
63
|
+
|
|
64
|
+
Interaction flow (the pause dance):
|
|
65
|
+
|
|
66
|
+
```gdscript
|
|
67
|
+
func interact(player: Node2D) -> void:
|
|
68
|
+
EventBus.dialogue_started.emit(npc_name)
|
|
69
|
+
GameManager.change_state(GameManager.GameState.DIALOGUE) # pauses tree
|
|
70
|
+
await _run_dialogue(start_index)
|
|
71
|
+
EventBus.dialogue_ended.emit()
|
|
72
|
+
GameManager.change_state(GameManager.GameState.PLAYING)
|
|
73
|
+
player.end_interaction()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
NPC = CharacterBody2D + CollisionShape2D + child Area2D named EXACTLY
|
|
77
|
+
"InteractionArea" on the interactions layer. Player entering shows a prompt;
|
|
78
|
+
interact key starts dialogue. When building NPCs in code, add the node tree
|
|
79
|
+
first, `add_child` it, THEN set script properties (npc_name, dialogue,
|
|
80
|
+
quest_to_give) - properties set before the script enters the tree can be
|
|
81
|
+
stomped by `_ready`.
|
|
82
|
+
|
|
83
|
+
DialogueBox: typewriter at 40 chars/sec, click skips to full text, click
|
|
84
|
+
again advances, choices render as buttons. Must be process_mode ALWAYS (tree
|
|
85
|
+
is paused during dialogue).
|
|
86
|
+
|
|
87
|
+
## Quests
|
|
88
|
+
|
|
89
|
+
QuestData: id, title, description, objectives array
|
|
90
|
+
(`{"id": "kill_slimes", "description": "Kill 3 slimes", "target": 3}`),
|
|
91
|
+
xp_reward, gold_reward, prerequisite_quest_id.
|
|
92
|
+
|
|
93
|
+
QuestManager (autoload) API: `start_quest(data)`,
|
|
94
|
+
`progress_objective(quest_id, objective_id, amount = 1)`,
|
|
95
|
+
`is_quest_active/complete(id)`, `get_objective_progress(id, obj_id)`. When
|
|
96
|
+
all objectives hit target the quest auto-completes, grants rewards, and
|
|
97
|
+
emits `quest_completed`.
|
|
98
|
+
|
|
99
|
+
Wiring kills to quests happens in the LEVEL script, not the enemy:
|
|
100
|
+
|
|
101
|
+
```gdscript
|
|
102
|
+
func _ready() -> void:
|
|
103
|
+
EventBus.enemy_killed.connect(_on_enemy_killed)
|
|
104
|
+
|
|
105
|
+
func _on_enemy_killed(_enemy: Node2D, _xp: int) -> void:
|
|
106
|
+
QuestManager.progress_objective("slay_monsters", "kill_enemies")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Enemies stay dumb; levels decide what counts.
|
|
110
|
+
|
|
111
|
+
## Inventory and items
|
|
112
|
+
|
|
113
|
+
InventoryManager (autoload): 20 item slots, 3 equipment slots (weapon,
|
|
114
|
+
armor, accessory), gold. `add_item()` returns false when full;
|
|
115
|
+
`use_item()` only works on consumables; `equip_item()` auto-routes by the
|
|
116
|
+
item's slot; `get_total_stat_bonus("attack")` sums equipment bonuses for the
|
|
117
|
+
stats pipeline; `clear()` on restart.
|
|
118
|
+
|
|
119
|
+
ItemData: ItemType (CONSUMABLE/WEAPON/ARMOR/ACCESSORY/KEY_ITEM), Rarity
|
|
120
|
+
(COMMON/UNCOMMON/RARE/EPIC with white/green/blue/purple colors),
|
|
121
|
+
`stat_bonuses: Dictionary` ("attack" -> 5), heal_amount, buy/sell price.
|
|
122
|
+
|
|
123
|
+
## Loot
|
|
124
|
+
|
|
125
|
+
- LootTable: guaranteed gold range + guaranteed XP, then `drop_chance` roll
|
|
126
|
+
over weighted entries (`{"item", "weight", "min_count", "max_count"}`).
|
|
127
|
+
`table.roll()` returns the drops.
|
|
128
|
+
- PickupOrb: XP/gold orbs from dead enemies, magnet-pull within 100px,
|
|
129
|
+
accelerating toward the player.
|
|
130
|
+
- TreasureChest: interactable static body - guaranteed item + gold orbs +
|
|
131
|
+
a loot table roll on open.
|
|
132
|
+
- ItemDrop: dropped items render with a rarity glow.
|
|
133
|
+
|
|
134
|
+
## Perks (level-up progression)
|
|
135
|
+
|
|
136
|
+
PerkData: id, name, description (supports a `{value}` token), max_level 3,
|
|
137
|
+
`stat_modifiers: Dictionary`, `special_effect: String` + effect_value for
|
|
138
|
+
non-stat perks. Built-in pool:
|
|
139
|
+
|
|
140
|
+
| ID | Effect per level |
|
|
141
|
+
|----|------|
|
|
142
|
+
| attack_up | +3 attack |
|
|
143
|
+
| speed_boost | +20 speed |
|
|
144
|
+
| thick_skin | +2 defense |
|
|
145
|
+
| vampirism | 3% lifesteal |
|
|
146
|
+
| burning_strikes | apply burn on hit |
|
|
147
|
+
| frost_touch | 30% slow on hit |
|
|
148
|
+
| critical_eye | +5% crit |
|
|
149
|
+
| multishot | +1 projectile |
|
|
150
|
+
| thorns | 20% reflect |
|
|
151
|
+
| regeneration | 1 HP/sec |
|
|
152
|
+
| xp_boost | +25% XP |
|
|
153
|
+
| last_stand | +50% damage below 30% HP |
|
|
154
|
+
|
|
155
|
+
PerkPool: `roll_selection(3)` (unowned or upgradable only),
|
|
156
|
+
`acquire_perk()`, `get_stat_bonus("attack")`, `has_effect("lifesteal")` +
|
|
157
|
+
`get_effect_value()`, `reset()` per run.
|
|
158
|
+
|
|
159
|
+
Level-up flow: `stats.add_xp()` true -> emit `player_leveled_up` then
|
|
160
|
+
`level_up_ready` -> PerkSelectionUI shows 3 cards (LEVEL_UP state pauses
|
|
161
|
+
tree) -> pick emits `perk_selected` -> player acquires and applies stat
|
|
162
|
+
modifiers via `stats.add_modifier` -> resume. Special effects (lifesteal,
|
|
163
|
+
thorns, multishot) are checked at their point of use via
|
|
164
|
+
`pool.has_effect()`, not applied as stats.
|
|
165
|
+
|
|
166
|
+
## Status effects
|
|
167
|
+
|
|
168
|
+
StatusEffect base (Node): effect_id, duration 3s, tick_interval 1s,
|
|
169
|
+
max_stacks 3. Override `tick()`, `on_expire()`, `on_max_stacks()`,
|
|
170
|
+
`get_speed_multiplier()`. `add_stack()` refreshes duration.
|
|
171
|
+
|
|
172
|
+
EffectManager attaches to any entity: `apply_effect(BurnEffect, entity)`
|
|
173
|
+
handles stacking, `get_speed_multiplier()` returns the product of all active
|
|
174
|
+
effects (feed it into movement), `remove_effect/remove_all`.
|
|
175
|
+
|
|
176
|
+
Built-ins and their max-stack payoffs (the interesting design bit -
|
|
177
|
+
stacking to cap triggers a burst):
|
|
178
|
+
|
|
179
|
+
- Burn: 3 dmg per 0.5s, 5 stacks; at cap, a 15x damage burst.
|
|
180
|
+
- Freeze: 30% slow per stack, 3 stacks; at cap, full 2s stun.
|
|
181
|
+
- Poison: 2 dmg/s, 3 stacks; each new stack extends duration 2s; supports
|
|
182
|
+
lifesteal back to the source.
|
|
183
|
+
|
|
184
|
+
## Juice quick reference
|
|
185
|
+
|
|
186
|
+
```gdscript
|
|
187
|
+
Juice.shake(4.0) # camera shake
|
|
188
|
+
Juice.hitstop(0.05) # freeze frame on big hits
|
|
189
|
+
Juice.flash_node(enemy, Color.WHITE, 0.1)
|
|
190
|
+
Juice.spawn_float_text(node, "+50 XP", Color.GOLD)
|
|
191
|
+
Juice.spawn_hit_particles(pos, Color.RED, 6)
|
|
192
|
+
Juice.bounce(node, 1.2) # scale bounce
|
|
193
|
+
Juice.register_camera(camera) # once, from player _ready()
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
If `register_camera` is never called, shake silently does nothing - a
|
|
197
|
+
classic "why is there no juice" bug.
|
|
198
|
+
|
|
199
|
+
## HUD and notifications
|
|
200
|
+
|
|
201
|
+
HUD subscribes to EventBus only: health/energy/XP bars, level, gold, active
|
|
202
|
+
quest list, and a notification label (fade in, 2s hold, fade out) driven by
|
|
203
|
+
`EventBus.notification_requested.emit("Quest complete!", Color.GOLD)`.
|
|
204
|
+
HUD also owns pause and inventory toggles.
|
|
205
|
+
|
|
206
|
+
## Audio
|
|
207
|
+
|
|
208
|
+
AudioManager: `play_music(path)` crossfades (pass false for hard cut),
|
|
209
|
+
`play_sfx(stream, pitch_variation)`, `play_sfx_at(stream, pos, variation)`
|
|
210
|
+
positional. Auto-creates Music and SFX buses if missing. Town theme in the
|
|
211
|
+
hub, battle music in combat areas, switch on `scene_ready`.
|
|
212
|
+
|
|
213
|
+
## Save/load
|
|
214
|
+
|
|
215
|
+
GameManager saves to `user://savegame.cfg` (ConfigFile): current level path
|
|
216
|
+
+ spawn position, player stats (level, XP, health, attack, defense),
|
|
217
|
+
inventory + equipment as resource paths, gold, quest state (active with
|
|
218
|
+
progress, completed ids). `save_game()` from the pause menu,
|
|
219
|
+
`load_game() -> bool` from Continue, `has_save()` gates the button.
|
|
220
|
+
`restart_game()` clears inventory, resets perks, returns to town.
|
|
221
|
+
|
|
222
|
+
Save items by resource path, not serialized objects - paths survive schema
|
|
223
|
+
changes; serialized Resources do not.
|
|
224
|
+
|
|
225
|
+
## Order of content work
|
|
226
|
+
|
|
227
|
+
1. Enemy roster (CHASE first, then RANGED; TANK/SWARM/BOSS after combat
|
|
228
|
+
feels right) + spawner in the forest level.
|
|
229
|
+
2. Loot orbs + level-up perks (progression pull).
|
|
230
|
+
3. One NPC with dialogue in town, one kill quest wired through the level.
|
|
231
|
+
4. Inventory + a treasure chest + equipment stat bonuses.
|
|
232
|
+
5. Status effects on weapons/perks (burning_strikes, frost_touch).
|
|
233
|
+
6. Boss + dungeon kill gate, then save/load last - it serializes everything
|
|
234
|
+
above, so build it when the shapes are stable.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# 2D Action RPG - Core
|
|
2
|
+
|
|
3
|
+
Top-down action RPG blueprint: event-driven architecture, player controller,
|
|
4
|
+
stats pipeline, combat components, weapons, and area transitions. NPCs,
|
|
5
|
+
dialogue, quests, inventory, perks, status effects, and enemies live in
|
|
6
|
+
`2d-rpg-content.md`.
|
|
7
|
+
|
|
8
|
+
## Minimum playable base (build this first)
|
|
9
|
+
|
|
10
|
+
One level extending LevelBase: player with 8-way movement + one sword swing +
|
|
11
|
+
dodge roll, a couple of chase enemies with `take_hit()`, HUD health bar. Prove
|
|
12
|
+
"move, hit enemy, get hit, see numbers" before any dialogue or quest exists.
|
|
13
|
+
|
|
14
|
+
Two rules for every scene:
|
|
15
|
+
|
|
16
|
+
- Dynamic viewport: `display/window/stretch/mode = "canvas_items"`,
|
|
17
|
+
`stretch/aspect = "expand"`. UI via Control anchors; compute spawn points and
|
|
18
|
+
bounds from `get_viewport_rect().size`, never hardcoded 1920x1080.
|
|
19
|
+
- Playable slice: every input gets feedback through the Juice autoload (shake,
|
|
20
|
+
flash, float text, hitstop). A silent build fails even if every system works.
|
|
21
|
+
|
|
22
|
+
## Architecture: modules + EventBus
|
|
23
|
+
|
|
24
|
+
Systems NEVER reference each other directly. All cross-system communication
|
|
25
|
+
flows through EventBus, a global signal bus autoload. Each module can be added
|
|
26
|
+
or removed without breaking others.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
core/
|
|
30
|
+
autoloads/{event_bus,game_manager}.gd
|
|
31
|
+
levels/level_base.gd
|
|
32
|
+
player/{player.gd,player.tscn}
|
|
33
|
+
modules/
|
|
34
|
+
audio/audio_manager.gd
|
|
35
|
+
combat/{hitbox_component,hurtbox_component,knockback_component,
|
|
36
|
+
stats,modified_value}.gd
|
|
37
|
+
dialogue/{dialogue_box,dialogue_data,npc}.gd
|
|
38
|
+
enemies/{enemy_base,enemy_data,enemy_database,enemy_spawner}.gd
|
|
39
|
+
effects/{status_effect,effect_manager,burn_effect,freeze_effect,poison_effect}.gd
|
|
40
|
+
inventory/{inventory_manager,inventory_ui,item_data}.gd
|
|
41
|
+
juice/juice.gd
|
|
42
|
+
loot/{area_transition,item_drop,loot_table,pickup_orb,treasure_chest}.gd
|
|
43
|
+
progression/{perk_data,perk_database,perk_pool,perk_selection_ui}.gd
|
|
44
|
+
quests/{quest_data,quest_manager}.gd
|
|
45
|
+
weapons/{weapon_base,weapon_data,weapon_manager,projectile}.gd
|
|
46
|
+
weapons/weapons/{sword,bow,staff,orb}.gd
|
|
47
|
+
content/levels/{town,forest,dungeon}.gd
|
|
48
|
+
ui/{hud/hud.gd, menus/{main_menu,pause_menu,game_over}.gd}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Six autoloads, in this order: EventBus, GameManager, AudioManager,
|
|
52
|
+
QuestManager, Juice, InventoryManager.
|
|
53
|
+
|
|
54
|
+
### EventBus signal groups (define up front)
|
|
55
|
+
|
|
56
|
+
Player: `player_health_changed`, `player_energy_changed`, `player_died`,
|
|
57
|
+
`player_leveled_up`, `player_xp_gained`. Combat: `damage_dealt`,
|
|
58
|
+
`enemy_killed(enemy, xp_value)`, `player_attack_executed`. Interaction:
|
|
59
|
+
`interaction_available/unavailable`, `dialogue_started/ended`. Inventory:
|
|
60
|
+
`item_picked_up/used/equipped/unequipped`, `gold_changed`. Quests:
|
|
61
|
+
`quest_started/updated/completed`, `quest_objective_progressed`. Progression:
|
|
62
|
+
`level_up_ready`, `perk_selected`, `perk_applied`. Scene:
|
|
63
|
+
`scene_transition_started/completed`, `scene_ready`. Spawner:
|
|
64
|
+
`wave_started/cleared`. UI: `notification_requested(text, color)`,
|
|
65
|
+
`boss_health_changed`.
|
|
66
|
+
|
|
67
|
+
## Physics layers and input
|
|
68
|
+
|
|
69
|
+
| Layer | Name |
|
|
70
|
+
|-------|------|
|
|
71
|
+
| 1 | terrain |
|
|
72
|
+
| 2 | player |
|
|
73
|
+
| 3 | enemies |
|
|
74
|
+
| 4 | player_hitbox |
|
|
75
|
+
| 5 | enemy_hitbox |
|
|
76
|
+
| 6 | interactions |
|
|
77
|
+
| 7 | pickups |
|
|
78
|
+
|
|
79
|
+
Hitbox on 4/5, hurtbox monitors the opposite side's layer.
|
|
80
|
+
|
|
81
|
+
Input map (9 actions): move_up/down/left/right (WASD + arrows + stick), attack
|
|
82
|
+
(LMB/Space), dodge (RMB/Shift), interact (E), pause (Esc), inventory (I).
|
|
83
|
+
|
|
84
|
+
## GameManager: states and transitions
|
|
85
|
+
|
|
86
|
+
```gdscript
|
|
87
|
+
enum GameState { MAIN_MENU, PLAYING, PAUSED, DIALOGUE, INVENTORY, LEVEL_UP, GAME_OVER }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Everything except MAIN_MENU/PLAYING pauses the tree; `change_state()` handles
|
|
91
|
+
pause/unpause. Gate gameplay input on `GameManager.is_gameplay_active()`.
|
|
92
|
+
|
|
93
|
+
Scene transition flow (fade overlay owned by GameManager):
|
|
94
|
+
`transition_to_scene(path, spawn_point, spawn_direction)` -> fade to black
|
|
95
|
+
0.3s -> emit `scene_transition_started` -> `change_scene_to_file` -> wait TWO
|
|
96
|
+
process frames -> emit `scene_ready`, state PLAYING -> fade in -> emit
|
|
97
|
+
`scene_transition_completed`. The player reads
|
|
98
|
+
`GameManager.player_spawn_point` / `player_spawn_direction` in `_ready()`.
|
|
99
|
+
Guard with an `_is_transitioning` flag; never call transition from inside a
|
|
100
|
+
transition callback.
|
|
101
|
+
|
|
102
|
+
Area transitions are trivial once this exists - an Area2D with three exports
|
|
103
|
+
(`target_scene`, `spawn_point`, `spawn_direction`) whose `body_entered` calls
|
|
104
|
+
`GameManager.transition_to_scene()`. Place at map edges and doorways.
|
|
105
|
+
|
|
106
|
+
## Player controller
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Player (CharacterBody2D, layer 2, group "player")
|
|
110
|
+
CollisionShape2D (circle, radius 8)
|
|
111
|
+
WeaponManager (Node2D)
|
|
112
|
+
HurtboxComponent (Area2D)
|
|
113
|
+
CollisionShape2D (circle, radius 10)
|
|
114
|
+
InteractionArea (Area2D)
|
|
115
|
+
CollisionShape2D (circle, radius 30)
|
|
116
|
+
Camera2D
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
State machine: `IDLE, MOVE, ATTACK, DODGE, HURT, DEAD, INTERACT`.
|
|
120
|
+
|
|
121
|
+
```gdscript
|
|
122
|
+
var input := Input.get_vector("move_left", "move_right", "move_up", "move_down")
|
|
123
|
+
if input.length() > 0.1:
|
|
124
|
+
facing = input.normalized()
|
|
125
|
+
velocity = facing * stats.get_speed()
|
|
126
|
+
state = State.MOVE
|
|
127
|
+
else:
|
|
128
|
+
velocity = velocity.move_toward(Vector2.ZERO, stats.get_speed() * 10.0 * delta)
|
|
129
|
+
state = State.IDLE
|
|
130
|
+
move_and_slide()
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- Attack: manual aims at mouse direction; optional auto_attack targets the
|
|
134
|
+
nearest enemy within 250px. Delegate to
|
|
135
|
+
`weapon_manager.attack(direction)` gated by `can_attack()`.
|
|
136
|
+
- Dodge roll: costs 20 energy (`stats.use_energy` returns false if broke),
|
|
137
|
+
0.25s duration, invincible for the duration, fading trail rects.
|
|
138
|
+
- Hurt: ignore if invincible/dead/dodging. `stats.take_damage`, 0.5s i-frames,
|
|
139
|
+
knockback velocity, `Juice.shake(5)`, red flash, float text, emit
|
|
140
|
+
`player_health_changed`.
|
|
141
|
+
|
|
142
|
+
## Stats: the ModifiedValue pipeline
|
|
143
|
+
|
|
144
|
+
`final = (base + additive_sum) * (1.0 + multiplicative_sum)`. Modifiers are
|
|
145
|
+
tagged with a source string so a whole buff can be removed in one call:
|
|
146
|
+
|
|
147
|
+
```gdscript
|
|
148
|
+
stats.add_modifier("attack", 5, "fire_buff")
|
|
149
|
+
stats.remove_modifier("attack", "fire_buff")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Stats resource: base_max_health 100, base_attack 10, base_defense 5,
|
|
153
|
+
base_speed 120, base_crit_chance 0.05, base_crit_multiplier 1.5. Key methods:
|
|
154
|
+
`take_damage()` returns `{damage, killed}`, `calculate_damage(is_crit)`,
|
|
155
|
+
`roll_crit()`, `add_xp()` returns true on level-up, `use_energy()`.
|
|
156
|
+
|
|
157
|
+
MUST call `stats.initialize()` in `_ready()` - forgetting it leaves the
|
|
158
|
+
pipeline null and everything crashes on first hit.
|
|
159
|
+
|
|
160
|
+
Level-up: +10 max HP, +2 attack, +1 defense, +3 speed, full heal.
|
|
161
|
+
XP needed = `50 + level * 30`.
|
|
162
|
+
|
|
163
|
+
## Combat components (reusable trio)
|
|
164
|
+
|
|
165
|
+
- HitboxComponent (Area2D): the damage dealer. `damage`, `knockback_force`,
|
|
166
|
+
`hit_cooldown` (per-target cooldown dictionary), `try_hit(target) -> bool`,
|
|
167
|
+
`activate()/deactivate()` (deactivate clears cooldowns). Emits
|
|
168
|
+
`hit_landed(target, damage, knockback)`.
|
|
169
|
+
- HurtboxComponent (Area2D): the receiver. `invincibility_duration` 0.3s,
|
|
170
|
+
emits `damage_received(amount, source, knockback)` via
|
|
171
|
+
`take_hit(damage, source, knockback)`.
|
|
172
|
+
- KnockbackComponent (Node on any CharacterBody2D): decaying velocity,
|
|
173
|
+
`resistance` 0-1, `decay_rate` 8. `apply_knockback(force)` then add
|
|
174
|
+
`get_knockback_velocity()` to movement.
|
|
175
|
+
|
|
176
|
+
Attach these to player, enemies, chests - anything hittable. This trio is
|
|
177
|
+
what keeps combat code out of entity scripts.
|
|
178
|
+
|
|
179
|
+
## Weapon system (data-driven)
|
|
180
|
+
|
|
181
|
+
WeaponData resource carries everything: type string, base_damage,
|
|
182
|
+
attack_speed (attacks/sec), knockback, range, piercing, projectile
|
|
183
|
+
speed/count/fan_angle, area_radius, orbit_count/speed, color. WeaponManager
|
|
184
|
+
(child of player) equips, switches (1-4 keys / scroll), and delegates attacks;
|
|
185
|
+
a `WEAPON_SCRIPTS` const maps type string -> preloaded script.
|
|
186
|
+
|
|
187
|
+
Four archetypes:
|
|
188
|
+
|
|
189
|
+
- Sword: 120-degree arc swing, alternating direction, ease-out for weight,
|
|
190
|
+
forward lunge, physics cone query, trail.
|
|
191
|
+
- Bow: 0.15s draw, then a fan of projectiles (count + fan_angle from data).
|
|
192
|
+
- Staff: homing bolts (homing_strength 3.0) + optional `trigger_nova()` AoE.
|
|
193
|
+
- Orb: passive - orbits the player, contact damage with per-enemy cooldown,
|
|
194
|
+
always active regardless of selected weapon.
|
|
195
|
+
|
|
196
|
+
One generic projectile script serves bow, staff, AND enemies: straight or
|
|
197
|
+
homing, piercing (tracks hit bodies to prevent double-hit), knockback, trail,
|
|
198
|
+
lifetime despawn. Configure per-shot via properties + `collision_mask` and a
|
|
199
|
+
`source_node` so it never hits its shooter.
|
|
200
|
+
|
|
201
|
+
## LevelBase and level authoring
|
|
202
|
+
|
|
203
|
+
All levels extend LevelBase, which does the boring wiring once:
|
|
204
|
+
|
|
205
|
+
```gdscript
|
|
206
|
+
func _ready() -> void:
|
|
207
|
+
_spawn_player() # instantiate player.tscn
|
|
208
|
+
_setup_ui() # HUD, PauseMenu, DialogueBox, Inventory, GameOver, PerkSelectionUI
|
|
209
|
+
_setup_connections()
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
A concrete level sets `level_name`, calls `super._ready()`, then adds its
|
|
213
|
+
spawner / NPCs / AreaTransitions. Town = safe hub with NPCs. Forest = combat
|
|
214
|
+
area with wave spawner. Dungeon = boss arena with a kill gate.
|
|
215
|
+
|
|
216
|
+
## Build order
|
|
217
|
+
|
|
218
|
+
1. project.godot: 6 autoloads in order, input map, layer names, 1920x1080
|
|
219
|
+
canvas_items/expand.
|
|
220
|
+
2. EventBus with the full signal catalog (empty handlers are fine).
|
|
221
|
+
3. Stats + ModifiedValue, then the combat component trio.
|
|
222
|
+
4. Player: movement -> dodge -> WeaponManager with sword only.
|
|
223
|
+
5. One chase enemy + Juice feedback + HUD health bar. Playable slice check.
|
|
224
|
+
6. GameManager transitions + LevelBase + a second area with AreaTransition.
|
|
225
|
+
7. Content systems from `2d-rpg-content.md`: enemies, NPCs, quests,
|
|
226
|
+
inventory, perks, effects, save/load.
|
|
227
|
+
|
|
228
|
+
## Traps
|
|
229
|
+
|
|
230
|
+
- Direct module references. Never `get_node("/root/QuestManager")` from a
|
|
231
|
+
weapon script - emit an EventBus signal. Modules only know EventBus and
|
|
232
|
+
their own data types; anything else rots into circular dependencies.
|
|
233
|
+
- Skipping `stats.initialize()`. Null pipeline, crash on first damage.
|
|
234
|
+
- Huge `_ready()` chains needing other nodes. Await
|
|
235
|
+
`get_tree().process_frame` or connect to `EventBus.scene_ready` instead.
|
|
236
|
+
- UI without `process_mode = ALWAYS`. Dialogue, inventory, level-up, and
|
|
237
|
+
pause all pause the tree; the UI must keep processing.
|
|
238
|
+
- Wrong hitbox/hurtbox layers. Hitbox layer 4/5, hurtbox masks the OPPOSING
|
|
239
|
+
side. Getting this backwards silently produces no hits.
|
|
240
|
+
- Renaming InteractionArea. NPC scripts access `$InteractionArea` by exact
|
|
241
|
+
name; a rename kills interaction with no error at parse time.
|