summer-engine 3.1.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 +102 -0
- package/README.md +18 -3
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/skills.js +20 -2
- 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 +18 -2
- 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/installer/agent-config.js +5 -3
- package/dist/installer/agent-table.d.ts +16 -0
- package/dist/installer/agent-table.js +52 -17
- 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/amp/README.md +1 -1
- package/integrations/crush/README.md +1 -1
- package/integrations/grok-build/README.md +1 -1
- package/integrations/hermes/README.md +1 -1
- package/integrations/kimi-code/README.md +1 -1
- package/integrations/mistral-vibe/README.md +1 -1
- package/integrations/opencode/README.md +1 -1
- package/integrations/rovo-dev/README.md +1 -1
- package/integrations/vscode-copilot/README.md +1 -1
- package/integrations/warp/README.md +1 -1
- package/integrations/windsurf/README.md +1 -1
- 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 +36 -14
- 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 +1 -1
- 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 +36 -14
- package/registry/generated/skills-registry.json +421 -124
- 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,254 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: in-game-purchases
|
|
3
|
+
description: "Sell items for Sparks and let short players buy Sparks in game via the Summer purchase sheet: checkout, Sparks purchase, results, testing, store policy."
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: gameplay
|
|
7
|
+
allowed-tools: Read Grep Write summer_get_script_errors summer_get_scene_tree
|
|
8
|
+
paths: ["**/*.gd"]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# In-game purchases with Sparks
|
|
12
|
+
|
|
13
|
+
Sparks are Summer's currency. A game never takes money, never holds a
|
|
14
|
+
balance and never approves a payment. It asks Summer, Summer shows its own
|
|
15
|
+
purchase sheet over the game, and the game reacts to the authoritative
|
|
16
|
+
result. There are two calls and they follow the same shape:
|
|
17
|
+
|
|
18
|
+
| The player wants to | Call | Validated by |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| buy an item with Sparks they have | `Summer.client.store.request_checkout(lines)` | the Summer ledger (debit) |
|
|
21
|
+
| buy Sparks because they are short | `Summer.client.store.request_sparks_purchase(minimum_sparks)` | the payment provider (Stripe or Apple), then the ledger credit |
|
|
22
|
+
|
|
23
|
+
## When to sell with Sparks
|
|
24
|
+
|
|
25
|
+
- Sell anything that persists across sessions or devices (cosmetics, unlocks,
|
|
26
|
+
passes) as a Summer item with a Sparks price. *Why:* the platform owns the
|
|
27
|
+
item, so ownership survives reinstalls and cannot be forged by editing the
|
|
28
|
+
game.
|
|
29
|
+
- Do not invent your own currency that is bought with money. *Why:* Summer
|
|
30
|
+
can only refund, protect and pay you out for Sparks purchases.
|
|
31
|
+
- Offer "Get Sparks" only when a purchase failed for lack of Sparks, or on an
|
|
32
|
+
explicit shop button. *Why:* the Summer sheet interrupts play; surprise
|
|
33
|
+
payment prompts read as pressure and get games reported.
|
|
34
|
+
|
|
35
|
+
## Rules and the reason for each
|
|
36
|
+
|
|
37
|
+
1. **Never grant an item from a button press or a receipt.** Unlock only when
|
|
38
|
+
`Summer.client.items` shows the player owns it. *Why:* the receipt proves
|
|
39
|
+
the debit; the inventory projection is what the platform guarantees the
|
|
40
|
+
player owns, including after a crash or on another device.
|
|
41
|
+
2. **Never send prices, amounts or providers.** The game passes an offer ID
|
|
42
|
+
and a quantity, or a Sparks hint. *Why:* the platform prices everything; a
|
|
43
|
+
client-supplied price is exactly what a cheater edits.
|
|
44
|
+
3. **One call is one purchase.** Every call creates a new identity. Never
|
|
45
|
+
call again to "retry" an unknown outcome. *Why:* the first request may
|
|
46
|
+
already be charged; the SDK and the Summer app recover it and deliver the
|
|
47
|
+
result on the signal.
|
|
48
|
+
4. **Handle results only in the status signals, and deduplicate by
|
|
49
|
+
`request_id` plus `version`.** Connect normally (not deferred) before
|
|
50
|
+
requesting. *Why:* results can arrive after a restart or twice after a
|
|
51
|
+
crash; a synchronous handler is how the SDK knows the result was delivered.
|
|
52
|
+
5. **Treat `manual_review` and pending as not done.** *Why:* the platform
|
|
53
|
+
has not decided yet; showing success or failure would be a guess.
|
|
54
|
+
6. **Check readiness and hide what is unavailable.**
|
|
55
|
+
`check_checkout_readiness()` and `check_sparks_purchase_readiness()` return
|
|
56
|
+
a `SummerResult`. *Why:* editor play, guest sessions and older Summer apps
|
|
57
|
+
do not offer purchases; a dead button is worse than no button.
|
|
58
|
+
|
|
59
|
+
## Result states
|
|
60
|
+
|
|
61
|
+
Both signals deliver a status with `state`:
|
|
62
|
+
|
|
63
|
+
| State | Meaning | What the game does |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `awaiting_approval` | the Summer sheet is open | nothing; optionally dim the shop |
|
|
66
|
+
| `pending` | payment or debit in progress | show "processing" |
|
|
67
|
+
| `purchased` | done; Sparks debited (checkout) or credited (Sparks) | checkout: refresh items and unlock from inventory. Sparks: say "Sparks added", let the player buy again |
|
|
68
|
+
| `declined` | the player closed the sheet | return to the shop quietly |
|
|
69
|
+
| `expired` | the sheet or payment timed out | same as declined |
|
|
70
|
+
| `rejected` | refused; read `error_code` | checkout with `insufficient_funds`: offer "Get Sparks". Sparks: `provider_rejected`, say the payment did not go through |
|
|
71
|
+
| `manual_review` | platform is checking it | "We are confirming this purchase" |
|
|
72
|
+
| `refunded` | Sparks only: a credited purchase was refunded or charged back (`full_refund`, `dispute_lost`) | nothing to undo; Summer already took the Sparks back. Refresh any balance display |
|
|
73
|
+
|
|
74
|
+
A Sparks request can also fail to start with `purchase_limit` (the player
|
|
75
|
+
reached the daily in-game Sparks limit) or `age_required` / `adult_required`
|
|
76
|
+
(only adults with a saved age can buy Sparks). Show a short message and no
|
|
77
|
+
retry button. *Why:* both limits are platform policy; retrying changes nothing.
|
|
78
|
+
|
|
79
|
+
The operation returned by each call also completes (`completed` signal) on
|
|
80
|
+
`purchased` (ok) or a terminal failure with codes such as `checkout_declined`
|
|
81
|
+
or `sparks_purchase_declined`. The status signal is the one to build UI on.
|
|
82
|
+
|
|
83
|
+
## Minimal complete example
|
|
84
|
+
|
|
85
|
+
```gdscript
|
|
86
|
+
extends Node
|
|
87
|
+
## A one-item shop. Buys `offer_id`; if the player is short, lets them buy
|
|
88
|
+
## Sparks through the Summer sheet, then they press Buy again.
|
|
89
|
+
|
|
90
|
+
@export var offer_id := "offer_sword"
|
|
91
|
+
@export var definition_id := "item_sword"
|
|
92
|
+
@export var price_sparks := 250 # display only; the platform charges its own price
|
|
93
|
+
|
|
94
|
+
signal message(text: String)
|
|
95
|
+
var _seen := {}
|
|
96
|
+
|
|
97
|
+
func _ready() -> void:
|
|
98
|
+
var store := Summer.client.store
|
|
99
|
+
store.checkout_status_changed.connect(_on_checkout)
|
|
100
|
+
store.sparks_purchase_status_changed.connect(_on_sparks)
|
|
101
|
+
Summer.client.items.refreshed.connect(_sync_owned)
|
|
102
|
+
_sync_owned()
|
|
103
|
+
|
|
104
|
+
func buy() -> void:
|
|
105
|
+
if not Summer.client.store.check_checkout_readiness().ok:
|
|
106
|
+
message.emit("The shop is not available here.")
|
|
107
|
+
return
|
|
108
|
+
var lines: Array[SummerCheckoutLine] = [SummerCheckoutLine.create(offer_id, 1)]
|
|
109
|
+
Summer.client.store.request_checkout(lines) # result arrives in _on_checkout
|
|
110
|
+
|
|
111
|
+
func get_sparks() -> void:
|
|
112
|
+
if not Summer.client.store.check_sparks_purchase_readiness().ok:
|
|
113
|
+
message.emit("Get Sparks from the Summer app.")
|
|
114
|
+
return
|
|
115
|
+
Summer.client.store.request_sparks_purchase(price_sparks) # hint only
|
|
116
|
+
|
|
117
|
+
func _first_time(status) -> bool:
|
|
118
|
+
var key := "%s:%d" % [status.request_id, status.version]
|
|
119
|
+
if _seen.has(key):
|
|
120
|
+
return false
|
|
121
|
+
_seen[key] = true
|
|
122
|
+
return true
|
|
123
|
+
|
|
124
|
+
func _on_checkout(status: SummerCheckoutStatus) -> void:
|
|
125
|
+
if not _first_time(status):
|
|
126
|
+
return
|
|
127
|
+
match status.state:
|
|
128
|
+
&"purchased":
|
|
129
|
+
Summer.client.items.refresh() # ownership comes from inventory
|
|
130
|
+
&"rejected":
|
|
131
|
+
if status.error_code == &"insufficient_funds":
|
|
132
|
+
message.emit("Not enough Sparks.") # show a Get Sparks button -> get_sparks()
|
|
133
|
+
else:
|
|
134
|
+
message.emit("The purchase was not completed.")
|
|
135
|
+
&"manual_review":
|
|
136
|
+
message.emit("We are confirming this purchase.")
|
|
137
|
+
&"declined", &"expired":
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
func _on_sparks(status: SummerSparksPurchaseStatus) -> void:
|
|
141
|
+
if not _first_time(status):
|
|
142
|
+
return
|
|
143
|
+
match status.state:
|
|
144
|
+
&"purchased":
|
|
145
|
+
message.emit("%d Sparks added." % status.receipt.sparks_credited)
|
|
146
|
+
&"rejected":
|
|
147
|
+
message.emit("The payment did not go through.")
|
|
148
|
+
&"manual_review":
|
|
149
|
+
message.emit("We are confirming your payment.")
|
|
150
|
+
|
|
151
|
+
func _sync_owned() -> void:
|
|
152
|
+
if Summer.client.items.has_owned_definition(definition_id):
|
|
153
|
+
message.emit("Sword unlocked.") # the only place the item is granted
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
After Sparks are added the player presses Buy again. That is a new checkout
|
|
157
|
+
and Summer shows it again. *Why:* the player confirms each spend separately.
|
|
158
|
+
|
|
159
|
+
## Multiplayer: granting an effect on a dedicated authority
|
|
160
|
+
|
|
161
|
+
Buying Sparks only adds Sparks to the wallet. Anything the Sparks buy
|
|
162
|
+
("Grow now", "Summon rain") is a separate checkout, and on a server the
|
|
163
|
+
authority must never grant it because a client says it paid. *Why:* a client
|
|
164
|
+
message is the one thing a cheater controls.
|
|
165
|
+
|
|
166
|
+
Model the effect as a consumable catalog item with instance ownership (each
|
|
167
|
+
purchase is a separate item, not a stack). *Why:* a stackable (quantity) item
|
|
168
|
+
shares one owned item ID across purchases, so the ID could not tell two
|
|
169
|
+
purchases apart. The client checks out; the receipt's `owned_item_ids` are item instances in
|
|
170
|
+
the player's Summer inventory. The client sends the authority only that owned
|
|
171
|
+
item ID. The authority consumes it with
|
|
172
|
+
`Summer.authority.items.consume(session, owned_item_id, idempotency_key)` and
|
|
173
|
+
applies the effect only after a successful receipt. *Why:* the platform ledger
|
|
174
|
+
consumes an instance item once, so the same item cannot be redeemed twice, in
|
|
175
|
+
this world or any other.
|
|
176
|
+
|
|
177
|
+
```gdscript
|
|
178
|
+
# On the dedicated authority. `redeemed` is saved with the world.
|
|
179
|
+
func redeem(session: SummerSession, owned_item_id: String) -> void:
|
|
180
|
+
if redeemed.has(owned_item_id):
|
|
181
|
+
return # a repeated message grants once
|
|
182
|
+
var key := "redeem_" + owned_item_id.sha256_text().substr(0, 40)
|
|
183
|
+
var op := Summer.authority.items.consume(session, owned_item_id, key)
|
|
184
|
+
var result: SummerResult = await op.get_result_or_completed_signal()
|
|
185
|
+
if not result.ok:
|
|
186
|
+
# item_not_found: not owned or already used, never grant.
|
|
187
|
+
# unavailable: retry later with the same key.
|
|
188
|
+
return
|
|
189
|
+
redeemed[owned_item_id] = op.command_id # record, apply, then save the world
|
|
190
|
+
apply_grow_now(session)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
- Derive the key from the owned item ID. *Why:* the same key returns the same
|
|
194
|
+
receipt (`op.replayed` is true), so a retry after a timeout never consumes
|
|
195
|
+
twice, and a different item with that key fails with `idempotency_conflict`.
|
|
196
|
+
- Apply the effect when the receipt arrives even if `op.replayed` is true, as
|
|
197
|
+
long as `redeemed` does not have the item. *Why:* a replay means the item was
|
|
198
|
+
consumed earlier but your world may have crashed before saving the effect.
|
|
199
|
+
- Save the world right after applying. *Why:* the receipt replay is tied to
|
|
200
|
+
the authority process; after a restart as a different process the same key
|
|
201
|
+
is a new consumption and the already-consumed item returns `item_not_found`.
|
|
202
|
+
|
|
203
|
+
## Idempotency
|
|
204
|
+
|
|
205
|
+
The SDK generates the request ID and replays it across transport failures and
|
|
206
|
+
app restarts; the platform credits a Sparks purchase once per provider
|
|
207
|
+
transaction and debits a checkout once per request. Your only job is the
|
|
208
|
+
`request_id:version` dedupe above, so a replayed signal does not show a
|
|
209
|
+
message or play an effect twice.
|
|
210
|
+
|
|
211
|
+
## Per-surface behaviour
|
|
212
|
+
|
|
213
|
+
- **Summer Games app on iOS:** a native Summer sheet over the game. Sparks are
|
|
214
|
+
bought with Apple In-App Purchase only.
|
|
215
|
+
- **Summer desktop app:** an overlay sheet; Sparks are paid with Stripe in the
|
|
216
|
+
browser, and the result returns to the game on the signal.
|
|
217
|
+
- **Web:** the same sheet in the page, paid with Stripe.
|
|
218
|
+
- **Editor play and local runs:** readiness is `unavailable`. Build the
|
|
219
|
+
unavailable path first.
|
|
220
|
+
|
|
221
|
+
The game code is identical on every surface.
|
|
222
|
+
|
|
223
|
+
## Testing
|
|
224
|
+
|
|
225
|
+
- Test against Summer staging, which runs payments in sandbox mode: Stripe
|
|
226
|
+
sandbox cards on web and desktop, an Apple Sandbox tester account on iOS test
|
|
227
|
+
builds. *Why:* no real money moves and results are identical to production.
|
|
228
|
+
- Walk every state: decline the sheet, let it expire, buy with too few
|
|
229
|
+
Sparks, buy Sparks, then buy the item. Kill the game while a payment is
|
|
230
|
+
pending and relaunch; the result must still arrive once.
|
|
231
|
+
- Verify the unlock comes from `items.refreshed`, not from the checkout
|
|
232
|
+
signal.
|
|
233
|
+
|
|
234
|
+
## Store policy notes
|
|
235
|
+
|
|
236
|
+
- Apple requires In-App Purchase for digital goods and currency sold inside
|
|
237
|
+
iOS apps. Summer's iOS sheet handles this; never add your own payment links,
|
|
238
|
+
web prices or "cheaper on the web" text inside the game. *Why:* it can get
|
|
239
|
+
the Summer app removed from the App Store for every game.
|
|
240
|
+
- Randomized paid rewards must disclose their odds before purchase (Apple and
|
|
241
|
+
most regions require it).
|
|
242
|
+
- Say what a purchase gives in plain words next to the price.
|
|
243
|
+
|
|
244
|
+
## Availability
|
|
245
|
+
|
|
246
|
+
`request_checkout` ships today. `request_sparks_purchase` needs a Summer
|
|
247
|
+
Engine and Summer app release that advertise `player.store.sparks_purchase@1`;
|
|
248
|
+
until then `check_sparks_purchase_readiness()` reports `unavailable` and the
|
|
249
|
+
example above falls back to "Get Sparks from the Summer app".
|
|
250
|
+
|
|
251
|
+
## See also
|
|
252
|
+
|
|
253
|
+
- multiplayer-state
|
|
254
|
+
- ui-basics
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
id: skill/in-game-purchases
|
|
2
|
+
kind: skill
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
summary: "Sell items for Sparks and let short players buy Sparks in game via the Summer purchase sheet: checkout, Sparks purchase, results, testing, store policy."
|
|
5
|
+
use_when:
|
|
6
|
+
- "adding a shop, buy button or paid item to a Summer game and wiring request_checkout"
|
|
7
|
+
- "a checkout was rejected with insufficient_funds and the player should be able to buy Sparks without leaving the game"
|
|
8
|
+
- "deciding whether an item should be sold for Sparks, or handling pending, declined, expired, rejected or manual_review purchase results"
|
|
9
|
+
do_not_use_when:
|
|
10
|
+
- "designing multiplayer state ownership unrelated to purchases (use multiplayer-state)"
|
|
11
|
+
facets:
|
|
12
|
+
lifecycle:
|
|
13
|
+
- build
|
|
14
|
+
domains:
|
|
15
|
+
- gameplay
|
|
16
|
+
- scripting
|
|
17
|
+
- game-design
|
|
18
|
+
modalities:
|
|
19
|
+
- scripts
|
|
20
|
+
compatibility:
|
|
21
|
+
engine: ">=4.6"
|
|
22
|
+
related:
|
|
23
|
+
skills:
|
|
24
|
+
- skill/multiplayer-state
|
|
25
|
+
- skill/ui-basics
|
|
26
|
+
source: official
|
|
27
|
+
license: MIT
|
|
28
|
+
status: preview
|
|
29
|
+
recommended: false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: instantiate-asset-pack
|
|
3
|
-
description:
|
|
3
|
+
description: "Import a whole create-asset-sheet pack into the scene: groups composites, sorts paint order, emits Sprite2D/Node2D/NinePatchRect ops to lay it out."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: 2d-assets
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: investigating-bugs
|
|
3
|
-
description:
|
|
3
|
+
description: "Systematic investigation of any bug, test failure, or unexpected behavior before proposing fixes."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Systematic Debugging
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lumera-single-image-scene-reconstruction
|
|
3
|
-
description: "
|
|
3
|
+
description: "Lumera-style single image to editable 3D scene — VLM-parsed object boxes and parametric lights, per-object meshes, HDR probe, .tscn assembly, refinement loop."
|
|
4
4
|
license: MIT
|
|
5
5
|
category: art-pipeline
|
|
6
6
|
tags:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: make-game
|
|
3
|
-
description: "
|
|
3
|
+
description: "Orchestration spine for 'make me a game': brainstorm, plan, scaffold, mechanics, art, audio, polish, verify, ship — delegates to specialist skills."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Codex, Windsurf, Gemini, OpenCode]
|
|
6
6
|
category: scene-and-project
|
|
@@ -27,6 +27,28 @@ This is the skill that runs end-to-end when a user says "make me a game." It doe
|
|
|
27
27
|
- User already has a working scaffold and wants to iterate → start at the relevant phase below, don't re-run the whole pipeline.
|
|
28
28
|
- User says "I want to use a template" → run `browse-templates` first; come back here only if they decide they want a custom build instead.
|
|
29
29
|
|
|
30
|
+
## Genre playbooks (`references/`)
|
|
31
|
+
|
|
32
|
+
Distilled blueprints for common genres live in this skill's `references/` folder: architecture, scene tree, collision layers, core-loop code, build order, traps. Each genre is two files — a core file and a systems/polish file. Read the core file during Phase 2 (Plan) so the build plan follows its architecture and build order; read the systems file only when Phase 4 reaches those mechanics. Never load more than the one genre you are building.
|
|
33
|
+
|
|
34
|
+
| Game | Core | Systems | Root / player / camera |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| Side-scrolling platformer | `references/2d-platformer.md` | `references/2d-platformer-polish.md` | Node2D / CharacterBody2D / Camera2D follow |
|
|
37
|
+
| Horde survivors / bullet heaven | `references/2d-survivors.md` | `references/2d-survivors-progression.md` | Node2D / CharacterBody2D / Camera2D on player |
|
|
38
|
+
| Top-down action RPG (also the base for puzzle, grid, tower defense) | `references/2d-rpg.md` | `references/2d-rpg-content.md` | Node2D / CharacterBody2D / Camera2D |
|
|
39
|
+
| First-person shooter | `references/3d-fps.md` | `references/3d-fps-systems.md` | Node3D / CharacterBody3D / Camera3D on Head |
|
|
40
|
+
| Third-person action / 3D platformer | `references/3d-third-person.md` | `references/3d-third-person-systems.md` | Node3D / CharacterBody3D / SpringArm3D |
|
|
41
|
+
| First-person horror | `references/3d-horror.md` | `references/3d-horror-systems.md` | Node3D / CharacterBody3D / Camera3D on Head |
|
|
42
|
+
|
|
43
|
+
No playbook (racing, sandbox, sim): build fresh through the phases below. The playbooks lean on these system skills — load one when its mechanic comes up: `character-movement`, `camera-rigs`, `combat-basics`, `save-load`, `ui-basics`, `setup-multiplayer`.
|
|
44
|
+
|
|
45
|
+
Two rules from the playbooks apply to every genre:
|
|
46
|
+
|
|
47
|
+
- **2D scales to the viewport.** Set `display/window/stretch/mode = canvas_items` and `stretch/aspect = expand`; UI uses Control anchors; code-positioned nodes compute from `get_viewport_rect().size`. Never hardcode 1920x1080.
|
|
48
|
+
- **Playable slice, not tech demo.** Name the core loop in one sentence; every input gets feedback (tween, sound, particle, flash, shake); progression is visible; a restart exists. The bar is "feels good for 30 seconds".
|
|
49
|
+
- **Fast first draft when someone is waiting.** The first playable version uses shapes, the palette and any template art, plus at most one or two generated assets the person specifically asked for; generation takes minutes, so it never blocks the first play. Say plainly that it is a first draft to get a feel for the game, that the art is not final, and ask if this is the direction they want. Generate the rest of the art in later passes.
|
|
50
|
+
- **Text the default font can show.** No emoji or symbols (▲ ◀ ★ ♥ ✓) in UI text: they render as boxes with hex codes. Use words or icons; see `ui-basics`.
|
|
51
|
+
|
|
30
52
|
## The Pipeline
|
|
31
53
|
|
|
32
54
|
```
|
|
@@ -153,7 +175,7 @@ For each mechanic in the plan, **in order**:
|
|
|
153
175
|
|
|
154
176
|
2. **Implement**:
|
|
155
177
|
- Add the nodes via `summer_add_node` / `summer_set_prop`. Use `scene-composition` if the structure is non-trivial.
|
|
156
|
-
- For movement-class mechanics, prefer the matching pre-built skill: `fps-controller`, `
|
|
178
|
+
- For movement-class mechanics, prefer the matching pre-built skill: `character-movement`, `fps-controller`, `multiplayer-movement`, etc. Follow the genre playbook's build order when one applies.
|
|
157
179
|
- Write GDScript with `gdscript-patterns` open.
|
|
158
180
|
|
|
159
181
|
3. **Verify**:
|
|
@@ -176,7 +198,7 @@ Repeat until every mechanic in the plan is implemented OR the user calls cut.
|
|
|
176
198
|
Skill: art-direction
|
|
177
199
|
```
|
|
178
200
|
|
|
179
|
-
That writes `.summer/art-bible.md` if it doesn't exist, then guides the lighting / material / palette pass. Use `3d-lighting` for the actual scene work.
|
|
201
|
+
That writes `.summer/art-bible.md` if it doesn't exist, then guides the lighting / material / palette pass. Use `3d-lighting` for the actual scene work. If the game plays in the browser or is exported for the web, it runs the Compatibility renderer only: follow `web-compatibility-look` instead of Forward+-only features (SDFGI, SSAO, SSR, volumetric fog), and keep `rendering/renderer/rendering_method` on `gl_compatibility`.
|
|
180
202
|
|
|
181
203
|
```
|
|
182
204
|
Skill: audio-direction
|
|
@@ -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`.
|