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.
Files changed (366) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +27 -5
  3. package/.codex-plugin/plugin.json +28 -6
  4. package/.cursor-plugin/plugin.json +27 -5
  5. package/.factory-plugin/plugin.json +27 -5
  6. package/.opencode/plugins/summer.js +1 -1
  7. package/CHANGELOG.md +102 -0
  8. package/README.md +18 -3
  9. package/assets/audit/scene_audit.gd +4136 -0
  10. package/assets/seeing/seeing_probe.gd +1873 -0
  11. package/dist/cli/commands/skills.js +20 -2
  12. package/dist/core/api-client.d.ts +9 -1
  13. package/dist/core/api-client.js +21 -4
  14. package/dist/core/capabilities/agent-playbook.js +46 -0
  15. package/dist/core/capabilities/audit/accept.d.ts +55 -0
  16. package/dist/core/capabilities/audit/accept.js +147 -0
  17. package/dist/core/capabilities/audit/args.d.ts +95 -0
  18. package/dist/core/capabilities/audit/args.js +89 -0
  19. package/dist/core/capabilities/audit/audit.d.ts +56 -0
  20. package/dist/core/capabilities/audit/audit.js +287 -0
  21. package/dist/core/capabilities/audit/frame.d.ts +14 -0
  22. package/dist/core/capabilities/audit/frame.js +35 -0
  23. package/dist/core/capabilities/audit/gaps.d.ts +96 -0
  24. package/dist/core/capabilities/audit/gaps.js +257 -0
  25. package/dist/core/capabilities/audit/judge.d.ts +162 -0
  26. package/dist/core/capabilities/audit/judge.js +1134 -0
  27. package/dist/core/capabilities/audit/math.d.ts +110 -0
  28. package/dist/core/capabilities/audit/math.js +263 -0
  29. package/dist/core/capabilities/audit/summary.d.ts +74 -0
  30. package/dist/core/capabilities/audit/summary.js +212 -0
  31. package/dist/core/capabilities/board.d.ts +31 -0
  32. package/dist/core/capabilities/board.js +43 -0
  33. package/dist/core/capabilities/camera-view.d.ts +4 -2
  34. package/dist/core/capabilities/camera-view.js +6 -3
  35. package/dist/core/capabilities/connect-signal.d.ts +85 -0
  36. package/dist/core/capabilities/connect-signal.js +285 -0
  37. package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
  38. package/dist/core/capabilities/engine-fallbacks.js +5 -0
  39. package/dist/core/capabilities/engine-ops.d.ts +5 -1
  40. package/dist/core/capabilities/engine-ops.js +0 -3
  41. package/dist/core/capabilities/engine-receipt.d.ts +12 -0
  42. package/dist/core/capabilities/engine-receipt.js +26 -0
  43. package/dist/core/capabilities/file-read.d.ts +64 -0
  44. package/dist/core/capabilities/file-read.js +325 -0
  45. package/dist/core/capabilities/game-task-plan.js +4 -2
  46. package/dist/core/capabilities/grep.d.ts +60 -0
  47. package/dist/core/capabilities/grep.js +190 -0
  48. package/dist/core/capabilities/inspect-node.d.ts +56 -0
  49. package/dist/core/capabilities/inspect-node.js +225 -0
  50. package/dist/core/capabilities/inspect-resource.d.ts +61 -0
  51. package/dist/core/capabilities/inspect-resource.js +376 -0
  52. package/dist/core/capabilities/math3d.d.ts +28 -0
  53. package/dist/core/capabilities/math3d.js +88 -0
  54. package/dist/core/capabilities/motion-generation.d.ts +62 -0
  55. package/dist/core/capabilities/motion-generation.js +138 -0
  56. package/dist/core/capabilities/placement-batch.d.ts +102 -0
  57. package/dist/core/capabilities/placement-batch.js +483 -0
  58. package/dist/core/capabilities/placement-math.d.ts +70 -0
  59. package/dist/core/capabilities/placement-math.js +209 -0
  60. package/dist/core/capabilities/placement-script.d.ts +42 -0
  61. package/dist/core/capabilities/placement-script.js +1096 -0
  62. package/dist/core/capabilities/placement.d.ts +485 -0
  63. package/dist/core/capabilities/placement.js +1731 -0
  64. package/dist/core/capabilities/project-context.d.ts +33 -2
  65. package/dist/core/capabilities/project-context.js +87 -4
  66. package/dist/core/capabilities/replace-node.d.ts +91 -0
  67. package/dist/core/capabilities/replace-node.js +423 -0
  68. package/dist/core/capabilities/runtime-control.d.ts +18 -2
  69. package/dist/core/capabilities/runtime-control.js +67 -2
  70. package/dist/core/capabilities/scene-batch.d.ts +63 -0
  71. package/dist/core/capabilities/scene-batch.js +394 -0
  72. package/dist/core/capabilities/scene-readback.d.ts +36 -0
  73. package/dist/core/capabilities/scene-readback.js +54 -0
  74. package/dist/core/capabilities/seeing/args.d.ts +348 -0
  75. package/dist/core/capabilities/seeing/args.js +178 -0
  76. package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
  77. package/dist/core/capabilities/seeing/candidates.js +282 -0
  78. package/dist/core/capabilities/seeing/math.d.ts +134 -0
  79. package/dist/core/capabilities/seeing/math.js +269 -0
  80. package/dist/core/capabilities/seeing/probe.d.ts +62 -0
  81. package/dist/core/capabilities/seeing/probe.js +188 -0
  82. package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
  83. package/dist/core/capabilities/seeing/scoring.js +639 -0
  84. package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
  85. package/dist/core/capabilities/seeing/seeing.js +1341 -0
  86. package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
  87. package/dist/core/capabilities/seeing/shot-store.js +204 -0
  88. package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
  89. package/dist/core/capabilities/surface-snap-mesh.js +425 -0
  90. package/dist/core/capabilities/surface-snap.d.ts +77 -0
  91. package/dist/core/capabilities/surface-snap.js +461 -0
  92. package/dist/core/capabilities/tool-dispatch.js +231 -72
  93. package/dist/core/capabilities/tscn.d.ts +85 -0
  94. package/dist/core/capabilities/tscn.js +391 -0
  95. package/dist/core/capabilities/world-snapshot.d.ts +58 -0
  96. package/dist/core/capabilities/world-snapshot.js +150 -0
  97. package/dist/core/capability-skew.d.ts +5 -5
  98. package/dist/core/capability-skew.js +14 -6
  99. package/dist/core/headless/worker-engine-client.d.ts +1 -1
  100. package/dist/core/headless/worker-engine-client.js +2 -2
  101. package/dist/core/library-read.d.ts +37 -5
  102. package/dist/core/library-read.js +264 -33
  103. package/dist/core/library-search.d.ts +2 -2
  104. package/dist/installer/agent-config.js +5 -3
  105. package/dist/installer/agent-table.d.ts +16 -0
  106. package/dist/installer/agent-table.js +52 -17
  107. package/dist/mcp/server.js +9 -3
  108. package/dist/mcp/tools/asset-tools.js +21 -3
  109. package/dist/mcp/tools/audit-tools.d.ts +23 -0
  110. package/dist/mcp/tools/audit-tools.js +60 -0
  111. package/dist/mcp/tools/debug-tools.js +3 -1
  112. package/dist/mcp/tools/file-tools.js +17 -4
  113. package/dist/mcp/tools/generate-tools.js +74 -66
  114. package/dist/mcp/tools/library-tools.js +2 -0
  115. package/dist/mcp/tools/perception-tools.js +6 -19
  116. package/dist/mcp/tools/placement-tools.d.ts +24 -0
  117. package/dist/mcp/tools/placement-tools.js +91 -0
  118. package/dist/mcp/tools/project-tools.js +18 -15
  119. package/dist/mcp/tools/scene-tools.js +71 -83
  120. package/dist/mcp/tools/seeing-tools.d.ts +24 -0
  121. package/dist/mcp/tools/seeing-tools.js +64 -0
  122. package/dist/mcp/tools/spatial-tools.js +17 -9
  123. package/dist/mcp/tools/visual-tools.js +59 -7
  124. package/gemini-extension.json +26 -4
  125. package/integrations/amp/README.md +1 -1
  126. package/integrations/crush/README.md +1 -1
  127. package/integrations/grok-build/README.md +1 -1
  128. package/integrations/hermes/README.md +1 -1
  129. package/integrations/kimi-code/README.md +1 -1
  130. package/integrations/mistral-vibe/README.md +1 -1
  131. package/integrations/opencode/README.md +1 -1
  132. package/integrations/rovo-dev/README.md +1 -1
  133. package/integrations/vscode-copilot/README.md +1 -1
  134. package/integrations/warp/README.md +1 -1
  135. package/integrations/windsurf/README.md +1 -1
  136. package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
  137. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  138. package/library/references/summer-folder/resource.yaml +1 -1
  139. package/library/references/summer-folder/summer-folder.md +1 -1
  140. package/library/skills/3d-lighting/SKILL.md +1 -1
  141. package/library/skills/README.md +36 -14
  142. package/library/skills/adaptive-music/SKILL.md +1 -1
  143. package/library/skills/agent-playtesting/SKILL.md +1 -1
  144. package/library/skills/ai-characters-in-games/SKILL.md +220 -0
  145. package/library/skills/ai-characters-in-games/resource.yaml +30 -0
  146. package/library/skills/ambient-bed/SKILL.md +1 -1
  147. package/library/skills/animated-loop/SKILL.md +1 -1
  148. package/library/skills/animation-tree/SKILL.md +1 -1
  149. package/library/skills/art-direction/SKILL.md +1 -1
  150. package/library/skills/asset-strategy/SKILL.md +2 -1
  151. package/library/skills/asset-strategy/resource.yaml +2 -1
  152. package/library/skills/audio-direction/SKILL.md +1 -1
  153. package/library/skills/auto-fire-targeting/SKILL.md +1 -1
  154. package/library/skills/brainstorm-game/SKILL.md +1 -1
  155. package/library/skills/brainstorming/SKILL.md +1 -1
  156. package/library/skills/browse-templates/SKILL.md +3 -3
  157. package/library/skills/browse-templates/resource.yaml +1 -1
  158. package/library/skills/camera-rigs/SKILL.md +128 -0
  159. package/library/skills/camera-rigs/resource.yaml +29 -0
  160. package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
  161. package/library/skills/character-animation-wiring/SKILL.md +1 -1
  162. package/library/skills/character-model/SKILL.md +1 -1
  163. package/library/skills/character-movement/SKILL.md +326 -0
  164. package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
  165. package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
  166. package/library/skills/character-movement/references/movement-3d.md +136 -0
  167. package/library/skills/character-movement/references/platformer-feel.md +83 -0
  168. package/library/skills/character-movement/resource.yaml +40 -0
  169. package/library/skills/character-portrait/SKILL.md +1 -1
  170. package/library/skills/cinematic-cutscene/SKILL.md +1 -1
  171. package/library/skills/combat-basics/SKILL.md +159 -0
  172. package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
  173. package/library/skills/combat-basics/resource.yaml +34 -0
  174. package/library/skills/concept-art/SKILL.md +1 -1
  175. package/library/skills/create-asset-sheet/SKILL.md +1 -1
  176. package/library/skills/debug/SKILL.md +1 -1
  177. package/library/skills/debugging-game-feel/SKILL.md +1 -1
  178. package/library/skills/design-level/SKILL.md +1 -1
  179. package/library/skills/design-mechanic/SKILL.md +1 -1
  180. package/library/skills/design-npc/SKILL.md +1 -1
  181. package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
  182. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
  183. package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
  184. package/library/skills/environment-kit/SKILL.md +1 -1
  185. package/library/skills/export-and-ship/SKILL.md +1 -1
  186. package/library/skills/fabricating-assets/SKILL.md +1 -1
  187. package/library/skills/facial-and-lipsync/SKILL.md +1 -1
  188. package/library/skills/fps-controller/SKILL.md +9 -7
  189. package/library/skills/fps-controller/resource.yaml +4 -1
  190. package/library/skills/game-feel/SKILL.md +1 -1
  191. package/library/skills/gameskill/SKILL.md +1 -1
  192. package/library/skills/gdscript-patterns/SKILL.md +1 -1
  193. package/library/skills/generate-motion/SKILL.md +9 -9
  194. package/library/skills/generate-motion/resource.yaml +2 -1
  195. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
  196. package/library/skills/headless-scripting/SKILL.md +1 -1
  197. package/library/skills/in-game-purchases/SKILL.md +254 -0
  198. package/library/skills/in-game-purchases/resource.yaml +29 -0
  199. package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
  200. package/library/skills/investigating-bugs/SKILL.md +1 -1
  201. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
  202. package/library/skills/make-game/SKILL.md +25 -3
  203. package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
  204. package/library/skills/make-game/references/2d-platformer.md +117 -0
  205. package/library/skills/make-game/references/2d-rpg-content.md +234 -0
  206. package/library/skills/make-game/references/2d-rpg.md +241 -0
  207. package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
  208. package/library/skills/make-game/references/2d-survivors.md +237 -0
  209. package/library/skills/make-game/references/3d-fps-systems.md +216 -0
  210. package/library/skills/make-game/references/3d-fps.md +177 -0
  211. package/library/skills/make-game/references/3d-horror-systems.md +186 -0
  212. package/library/skills/make-game/references/3d-horror.md +201 -0
  213. package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
  214. package/library/skills/make-game/references/3d-third-person.md +215 -0
  215. package/library/skills/make-game/resource.yaml +6 -1
  216. package/library/skills/multiplayer/SKILL.md +150 -0
  217. package/library/skills/multiplayer/resource.yaml +33 -0
  218. package/library/skills/multiplayer-movement/SKILL.md +510 -0
  219. package/library/skills/multiplayer-movement/files.md +501 -0
  220. package/library/skills/multiplayer-movement/resource.yaml +30 -0
  221. package/library/skills/multiplayer-project/SKILL.md +521 -0
  222. package/library/skills/multiplayer-project/files.md +448 -0
  223. package/library/skills/multiplayer-project/resource.yaml +36 -0
  224. package/library/skills/multiplayer-publish/SKILL.md +229 -0
  225. package/library/skills/multiplayer-publish/export-errors.md +89 -0
  226. package/library/skills/multiplayer-publish/resource.yaml +28 -0
  227. package/library/skills/multiplayer-state/SKILL.md +438 -0
  228. package/library/skills/multiplayer-state/files.md +592 -0
  229. package/library/skills/multiplayer-state/resource.yaml +35 -0
  230. package/library/skills/multiplayer-testing/SKILL.md +236 -0
  231. package/library/skills/multiplayer-testing/resource.yaml +39 -0
  232. package/library/skills/music-track/SKILL.md +1 -1
  233. package/library/skills/navigate-summer/SKILL.md +1 -1
  234. package/library/skills/new-project/SKILL.md +1 -1
  235. package/library/skills/organic-model/SKILL.md +1 -1
  236. package/library/skills/pixel-art/SKILL.md +1 -1
  237. package/library/skills/play/SKILL.md +1 -1
  238. package/library/skills/playtesting-a-feature/SKILL.md +1 -1
  239. package/library/skills/procedural-animation/SKILL.md +2 -2
  240. package/library/skills/procedural-animation/resource.yaml +1 -1
  241. package/library/skills/prop-model/SKILL.md +1 -1
  242. package/library/skills/psx-retro-rendering/SKILL.md +1 -1
  243. package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
  244. package/library/skills/remote-deploy/SKILL.md +1 -1
  245. package/library/skills/retarget/SKILL.md +1 -1
  246. package/library/skills/running-in-the-cloud/SKILL.md +1 -1
  247. package/library/skills/save-load/SKILL.md +156 -0
  248. package/library/skills/save-load/resource.yaml +27 -0
  249. package/library/skills/scene-composition/SKILL.md +1 -1
  250. package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
  251. package/library/skills/scene-scripting/SKILL.md +1 -1
  252. package/library/skills/scene-to-level/SKILL.md +1 -1
  253. package/library/skills/skill-create/SKILL.md +1 -1
  254. package/library/skills/skill-improve/SKILL.md +1 -1
  255. package/library/skills/skill-test/SKILL.md +1 -1
  256. package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
  257. package/library/skills/skybox-panorama/SKILL.md +1 -1
  258. package/library/skills/sound-effect/SKILL.md +1 -1
  259. package/library/skills/spatial-placement/SKILL.md +134 -3
  260. package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
  261. package/library/skills/spatial-placement/resource.yaml +16 -2
  262. package/library/skills/sprite-sheet/SKILL.md +1 -1
  263. package/library/skills/summer-analytics/SKILL.md +147 -0
  264. package/library/skills/summer-analytics/resource.yaml +28 -0
  265. package/library/skills/summer-friends/SKILL.md +226 -0
  266. package/library/skills/summer-friends/resource.yaml +28 -0
  267. package/library/skills/summer-leaderboards/SKILL.md +231 -0
  268. package/library/skills/summer-leaderboards/resource.yaml +30 -0
  269. package/library/skills/summer-match-results/SKILL.md +286 -0
  270. package/library/skills/summer-match-results/resource.yaml +32 -0
  271. package/library/skills/summer-matchmaking/SKILL.md +301 -0
  272. package/library/skills/summer-matchmaking/resource.yaml +32 -0
  273. package/library/skills/summer-parties/SKILL.md +253 -0
  274. package/library/skills/summer-parties/resource.yaml +30 -0
  275. package/library/skills/summer-player-data/SKILL.md +322 -0
  276. package/library/skills/summer-player-data/resource.yaml +30 -0
  277. package/library/skills/summer-store/SKILL.md +271 -0
  278. package/library/skills/summer-store/resource.yaml +29 -0
  279. package/library/skills/summer-world-chat/SKILL.md +195 -0
  280. package/library/skills/summer-world-chat/resource.yaml +30 -0
  281. package/library/skills/summer-world-saves/SKILL.md +284 -0
  282. package/library/skills/summer-world-saves/resource.yaml +29 -0
  283. package/library/skills/text-to-motion/SKILL.md +99 -0
  284. package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
  285. package/library/skills/text-to-motion/resource.yaml +41 -0
  286. package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
  287. package/library/skills/tileable-texture/SKILL.md +1 -1
  288. package/library/skills/trailer-shot/SKILL.md +1 -1
  289. package/library/skills/tune-performance/SKILL.md +1 -1
  290. package/library/skills/ui-basics/SKILL.md +10 -1
  291. package/library/skills/ui-graphics/SKILL.md +1 -1
  292. package/library/skills/use-widget-asset/SKILL.md +1 -1
  293. package/library/skills/using-summer/SKILL.md +2 -2
  294. package/library/skills/using-summer/resource.yaml +2 -2
  295. package/library/skills/vehicle-model/SKILL.md +1 -1
  296. package/library/skills/verification-before-completion/SKILL.md +1 -1
  297. package/library/skills/verifying-scenes/SKILL.md +76 -2
  298. package/library/skills/verifying-scenes/resource.yaml +13 -2
  299. package/library/skills/vfx-dissolve/SKILL.md +1 -1
  300. package/library/skills/vfx-fire/SKILL.md +1 -1
  301. package/library/skills/vfx-hit-spark/SKILL.md +1 -1
  302. package/library/skills/vfx-lightning/SKILL.md +1 -1
  303. package/library/skills/vfx-magic-glow/SKILL.md +1 -1
  304. package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
  305. package/library/skills/vfx-smoke/SKILL.md +1 -1
  306. package/library/skills/vfx-water-ripple/SKILL.md +1 -1
  307. package/library/skills/voice-line/SKILL.md +1 -1
  308. package/library/skills/web-and-touch-input/SKILL.md +38 -0
  309. package/library/skills/web-and-touch-input/resource.yaml +28 -0
  310. package/library/skills/web-compatibility-look/SKILL.md +62 -0
  311. package/library/skills/web-compatibility-look/resource.yaml +30 -0
  312. package/library/skills/world-building-3d/SKILL.md +8 -1
  313. package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
  314. package/library/skills/world-building-3d/resource.yaml +2 -1
  315. package/library/skills/writing-plans/SKILL.md +1 -1
  316. package/library/skills/writing-skills/SKILL.md +1 -1
  317. package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
  318. package/library/tools/attach-to-surface/resource.yaml +142 -0
  319. package/library/tools/batch/resource.yaml +9 -3
  320. package/library/tools/connect-ports/resource.yaml +120 -0
  321. package/library/tools/connect-signal/resource.yaml +3 -2
  322. package/library/tools/debug-views/resource.yaml +94 -0
  323. package/library/tools/frame-nodes/resource.yaml +104 -0
  324. package/library/tools/frame-shot/resource.yaml +134 -0
  325. package/library/tools/generate-motion/resource.yaml +44 -9
  326. package/library/tools/get-board/resource.yaml +50 -0
  327. package/library/tools/get-project-context/resource.yaml +13 -2
  328. package/library/tools/grep/resource.yaml +77 -0
  329. package/library/tools/inspect-asset/resource.yaml +71 -0
  330. package/library/tools/inspect-node/resource.yaml +9 -2
  331. package/library/tools/inspect-resource/resource.yaml +16 -5
  332. package/library/tools/instantiate-scene/resource.yaml +27 -2
  333. package/library/tools/measure/resource.yaml +96 -0
  334. package/library/tools/place-adjacent/resource.yaml +113 -0
  335. package/library/tools/play/resource.yaml +15 -2
  336. package/library/tools/raycast/resource.yaml +101 -0
  337. package/library/tools/read-file/resource.yaml +36 -2
  338. package/library/tools/read-library/resource.yaml +2 -2
  339. package/library/tools/repeat-along/resource.yaml +129 -0
  340. package/library/tools/replace-node/resource.yaml +11 -5
  341. package/library/tools/scene-audit/resource.yaml +144 -0
  342. package/library/tools/screenshot/resource.yaml +13 -2
  343. package/library/tools/search-assets/resource.yaml +18 -0
  344. package/library/tools/shot-sheet/resource.yaml +110 -0
  345. package/library/tools/snap-to-surface/resource.yaml +4 -1
  346. package/library/tools/world-snapshot/resource.yaml +24 -3
  347. package/library/tools/zoom/resource.yaml +105 -0
  348. package/package.json +1 -1
  349. package/registry/generated/aliases.json +17 -3
  350. package/registry/generated/counts.json +3 -3
  351. package/registry/generated/gemini-extension.json +26 -4
  352. package/registry/generated/index.json +2154 -469
  353. package/registry/generated/marketplace.claude.json +2 -2
  354. package/registry/generated/plugin.claude.json +27 -5
  355. package/registry/generated/plugin.codex.json +28 -6
  356. package/registry/generated/plugin.cursor.json +27 -5
  357. package/registry/generated/plugin.factory.json +27 -5
  358. package/registry/generated/skills-index.md +36 -14
  359. package/registry/generated/skills-registry.json +421 -124
  360. package/registry/generated/templates-registry.json +2 -2
  361. package/library/skills/host-authoritative-state/SKILL.md +0 -332
  362. package/library/skills/host-authoritative-state/resource.yaml +0 -28
  363. package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
  364. package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
  365. package/library/skills/setup-multiplayer/SKILL.md +0 -336
  366. package/library/skills/setup-multiplayer/resource.yaml +0 -28
@@ -0,0 +1,237 @@
1
+ # 2D Survivors - Core
2
+
3
+ Vampire Survivors-style auto-battler blueprint: arena, wave state machine, player,
4
+ and auto-firing weapons. The player only moves and dashes; weapons aim and fire
5
+ themselves. Waves, enemies, upgrades, shop, and evolution live in
6
+ `2d-survivors-progression.md`.
7
+
8
+ ## Minimum playable base (build this first)
9
+
10
+ One arena scene: player CharacterBody2D with WASD movement, one auto-firing
11
+ projectile weapon, a handful of chasing enemies, and visible feedback on every
12
+ hit (flash + damage number). Prove "move, enemies chase, weapon kills them"
13
+ before waves, shop, or characters exist.
14
+
15
+ Two rules for every scene:
16
+
17
+ - Dynamic viewport: `display/window/stretch/mode = "canvas_items"`,
18
+ `stretch/aspect = "expand"`. All UI uses Control anchors, never hardcoded
19
+ pixel positions. Spawn enemies relative to the player (650-900px out), never
20
+ from screen coordinates.
21
+ - Playable slice, not tech demo: every hit needs shake/flash/particles/damage
22
+ number. A silent wave where numbers move but nothing feels alive fails.
23
+
24
+ ## Architecture
25
+
26
+ ```
27
+ res://
28
+ scenes/
29
+ main.tscn # main menu (run entry point)
30
+ levels/arena.tscn # THE gameplay scene (owns everything)
31
+ player/player.tscn
32
+ enemies/enemy.tscn # one scene reused for ALL enemy types
33
+ enemies/enemy_projectile.tscn
34
+ pickups/pickup.tscn # gold coin / health orb
35
+ weapons/projectile.tscn
36
+ ui/character_select.tscn
37
+ scripts/
38
+ autoloads/{game_manager,audio_manager}.gd
39
+ player/player.gd
40
+ enemies/{enemy_base,enemy_spawner,enemy_projectile,elite_system}.gd
41
+ weapons/{projectile_weapon,projectile,orbital_weapon,area_weapon}.gd
42
+ weapons/evolved/{piercing_projectile_weapon,radiant_orbital_weapon,supernova_weapon}.gd
43
+ systems/{wave_data,upgrade_data,upgrade_manager,weapon_evolution,
44
+ item_data,item_manager,item_registry,character_data,
45
+ character_registry,pickup,camera_effects,arena_hazard}.gd
46
+ ui/{main_menu,character_select_ui,hud,level_up_ui,shop_ui,
47
+ pause_menu,game_over_ui}.gd
48
+ ```
49
+
50
+ Two autoloads only: `GameManager` (single source of truth for ALL run state -
51
+ stats, gold, XP, health, wave, signals) and `AudioManager` (pooled SFX + music).
52
+ Every system reads and writes GameManager; UI listens to its signals.
53
+
54
+ ## Physics layers
55
+
56
+ | Layer | Bit | Name |
57
+ |-------|-----|------|
58
+ | 1 | 1 | Player |
59
+ | 2 | 2 | Enemies |
60
+ | 3 | 4 | Pickups |
61
+ | 4 | 8 | PlayerProjectiles |
62
+ | 5 | 16 | Walls |
63
+ | 6 | 32 | EnemyProjectiles |
64
+
65
+ Wiring: Player body layer 1 mask 16 (walls only). Player Hurtbox layer 0 mask 34
66
+ (Enemies + EnemyProjectiles). PickupRange layer 0 mask 4. Enemies layer 2 mask 0.
67
+ Player projectiles layer 8 mask 2. Enemy projectiles layer 32 mask 1.
68
+
69
+ Input map: move_up/down/left/right (WASD + left stick), dash (Space + R2),
70
+ pause (Escape + Start).
71
+
72
+ ## Wave state machine (arena.gd)
73
+
74
+ The arena scene owns the player, spawner, all managers, and all UI CanvasLayers.
75
+ Everything wires together via signals in `_ready()`. Per wave, 4 phases:
76
+
77
+ ```
78
+ PRE_WAVE (1.5s announce) -> COMBAT (timed, 20-30s) ->
79
+ WAVE_CLEAR (2.5s: kill all, vacuum gold, slow-mo) ->
80
+ SHOPPING (buy/reroll/next) -> next PRE_WAVE
81
+ ```
82
+
83
+ 20 waves total, bosses at 10 and 20, victory after 20, death = game over.
84
+
85
+ ```gdscript
86
+ # arena.gd core
87
+ enum WaveState { PRE_WAVE, COMBAT, WAVE_CLEAR, SHOPPING }
88
+
89
+ func _physics_process(delta: float) -> void:
90
+ if GameManager.current_state != GameManager.State.PLAYING:
91
+ return
92
+ match _wave_state:
93
+ WaveState.PRE_WAVE:
94
+ _state_timer -= delta
95
+ if _state_timer <= 0.0: _start_combat()
96
+ WaveState.COMBAT:
97
+ _combat_time_remaining -= delta
98
+ hud.update_wave_timer(_combat_time_remaining)
99
+ if _combat_time_remaining <= 0.0: _start_wave_clear()
100
+ WaveState.WAVE_CLEAR:
101
+ _state_timer -= delta
102
+ if _state_timer <= 0.0: _start_shopping()
103
+ ```
104
+
105
+ Wave clear = spawner off, `kill_all_enemies()`, gold bonus (5 + wave*2),
106
+ flash + slow_motion(0.4, 0.8) + zoom pulse. GameManager pauses the tree for
107
+ LEVEL_UP / SHOPPING / PAUSED / GAME_OVER states and unpauses on PLAYING:
108
+
109
+ ```gdscript
110
+ func change_state(new_state: State) -> void:
111
+ current_state = new_state
112
+ state_changed.emit(new_state)
113
+ match new_state:
114
+ State.PLAYING: get_tree().paused = false
115
+ State.PAUSED, State.LEVEL_UP, State.SHOPPING, State.GAME_OVER:
116
+ get_tree().paused = true
117
+ ```
118
+
119
+ Arena is a 6000x6000 field: background ColorRect, StaticBody2D walls on layer 5
120
+ at +-3020, Line2D border for visibility. Player collides with walls; enemies
121
+ do not (spawn logic clamps them).
122
+
123
+ ## Player
124
+
125
+ ```
126
+ Player (CharacterBody2D, layer 1, mask 16/Walls)
127
+ CollisionShape2D (CircleShape2D radius 14)
128
+ Body (Polygon2D, arrow shape, character color)
129
+ Hurtbox (Area2D, layer 0, mask 34)
130
+ CollisionShape2D (CircleShape2D radius 12)
131
+ PickupRange (Area2D, layer 0, mask 4)
132
+ CollisionShape2D (CircleShape2D radius 140, driven by GameManager stat)
133
+ WeaponMount (Node2D) # weapons attach here
134
+ Camera2D (smoothing on, speed 4.0)
135
+ ```
136
+
137
+ Movement is trivial by design - all depth lives in the build:
138
+
139
+ ```gdscript
140
+ func _move() -> void:
141
+ var input := Input.get_vector("move_left", "move_right", "move_up", "move_down")
142
+ velocity = input * GameManager.player_speed
143
+ move_and_slide()
144
+ if input.length_squared() > 0.01:
145
+ body.rotation = input.angle()
146
+ ```
147
+
148
+ Dash: 0.14s invincible burst at 650px/s, 1.2s cooldown, afterimage ghost
149
+ (Polygon2D copy, tween alpha to 0 over 0.2s) every 0.025s. Invincibility timer =
150
+ dash duration + 0.08. HUD shows the cooldown bar.
151
+
152
+ Contact damage: do NOT use `area_entered` signals. Check
153
+ `hurtbox.get_overlapping_areas()` manually each `_physics_process` and apply
154
+ damage with a 0.6s invincibility window. Signals miss continuous contact;
155
+ polling + i-frames is reliable.
156
+
157
+ Player also handles: regen tick (1s interval, `GameManager.player_regen` HP),
158
+ pickup magnet (attract pickups inside PickupRange), and a `_weapons` Dictionary
159
+ keyed by weapon id - `add_or_upgrade_weapon(id)` upgrades if owned, else
160
+ instantiates the weapon script onto WeaponMount.
161
+
162
+ ## Weapons (auto-fire, no aiming)
163
+
164
+ All weapons are Node2D scripts on WeaponMount, firing from `_physics_process`.
165
+ Damage scales with `GameManager.player_damage_mult`, cooldowns divide by
166
+ `player_attack_speed_mult`, crits roll `player_crit_chance` for 2x. Three bases:
167
+
168
+ - Projectile: fires at nearest enemy within 800px (compare
169
+ `distance_squared_to` against 640000, never sqrt). Extra projectiles fan out
170
+ at 0.15 rad spread. If no target, retry in 0.1s instead of a full cooldown.
171
+ - Orbital: N shields orbit at radius 80, speed 3.5 rad/s. Per-enemy hit
172
+ cooldown Dictionary (0.4s keyed by instance id) so one orbit does not
173
+ multi-hit the same enemy every frame.
174
+ - Area (nova): every 3.0s damage all enemies within radius 120 (group scan +
175
+ distance_squared), show an expanding ring tween.
176
+
177
+ ```gdscript
178
+ func _find_nearest_enemy() -> Node2D:
179
+ var nearest: Node2D = null
180
+ var best := MAX_TARGET_RANGE_SQ
181
+ for enemy in get_tree().get_nodes_in_group("enemies"):
182
+ if not is_instance_valid(enemy): continue
183
+ var d := global_position.distance_squared_to(enemy.global_position)
184
+ if d < best:
185
+ best = d
186
+ nearest = enemy
187
+ return nearest
188
+ ```
189
+
190
+ Group scan is fine to ~250 enemies (the max_alive cap); past that use spatial
191
+ hashing.
192
+
193
+ ## Pickups and XP
194
+
195
+ XP is NOT a physical pickup - it auto-earns on kill (`GameManager.add_xp`,
196
+ amount = enemy xp_value). Gold and health orbs ARE physical Area2D pickups in
197
+ the "pickups" group with magnet behavior: once `attract()` is called (player
198
+ range overlap, or level-up vacuums all), accelerate toward the player at up to
199
+ 650px/s and collect within 12px. Level-up curve:
200
+ `xp_needed = 5 + level*level + level*3`.
201
+
202
+ ## Camera effects (the juice layer)
203
+
204
+ `camera_effects.gd` attaches as a child of the player's Camera2D. Provides
205
+ `screen_shake(intensity, duration)` (tween_method randomized offset decaying to
206
+ zero), `screen_flash(color, duration)`, `slow_motion(scale, duration)` (set
207
+ `Engine.time_scale`, restore via tween with `set_ignore_time_scale(true)`),
208
+ `zoom_pulse`, and a low-HP vignette shader that fades in below 35% health.
209
+ Register it on GameManager (`GameManager.camera_fx`) so every system can call it.
210
+
211
+ ## Build order
212
+
213
+ 1. project.godot: autoloads, input map, layer names, main scene, 1920x1080
214
+ canvas_items/expand stretch. Audio buses: Master, Music, SFX.
215
+ 2. Scenes smallest-first: pickup, projectile, enemy_projectile, enemy, player,
216
+ character_select, main, arena.
217
+ 3. Base loop: player move + dash, projectile weapon, walker enemies chasing.
218
+ 4. GameManager stats + HUD (health, gold, XP bar, wave timer, kills, dash bar).
219
+ 5. Wave state machine + spawner + wave data (progression file).
220
+ 6. Level-up upgrades, shop, elites, evolution, characters (progression file).
221
+
222
+ ## Traps
223
+
224
+ - Physics-based enemies. Never RigidBody2D or move_and_slide for enemies -
225
+ direct `position +=` on Area2D. Hundreds of colliding bodies kill perf;
226
+ enemies only need to chase, not collide with each other.
227
+ - Heavy enemy scenes. One Area2D + CollisionShape2D + Polygon2D + ProgressBar.
228
+ No AnimationPlayer, no particles, no per-enemy shader materials - you will
229
+ have 200+ alive.
230
+ - UI without `process_mode = ALWAYS`. The tree pauses during level-up, shop,
231
+ and game over; without ALWAYS, no button responds and the game soft-locks.
232
+ - Manual aim. The entire input surface is move + dash. Weapons auto-target;
233
+ adding aim breaks the genre.
234
+ - Editor-connected signals on runtime-instantiated scenes. Connect in `_ready()`
235
+ via code for enemies/projectiles/pickups - editor connections on templates
236
+ are fragile and undebuggable.
237
+ - On-screen spawns. Always 650-900px from the player, clamped to arena bounds.
@@ -0,0 +1,216 @@
1
+ # 3D FPS - Systems
2
+
3
+ The combat and interaction layer on top of `3d-fps.md`: hitscan and
4
+ projectile shooting, the data-driven weapon system, raycast interaction, and
5
+ the HUD.
6
+
7
+ ## Shooting: hitscan vs projectile
8
+
9
+ Hitscan (raycast) for rifles/pistols, projectiles for rockets/grenades.
10
+ Most weapons should be hitscan - instant, cheap, reliable.
11
+
12
+ ### Hitscan
13
+
14
+ Uses the player's ShootRay (RayCast3D under Camera3D, target (0,0,-100),
15
+ mask 6 = Environment + Enemy):
16
+
17
+ ```gdscript
18
+ func _shoot() -> void:
19
+ if not can_shoot:
20
+ return
21
+ can_shoot = false
22
+ shoot_cooldown = 0.1 # fire rate
23
+ shoot_ray.force_raycast_update()
24
+ if shoot_ray.is_colliding():
25
+ var collider := shoot_ray.get_collider()
26
+ _spawn_hit_effect(shoot_ray.get_collision_point(), shoot_ray.get_collision_normal())
27
+ if collider.has_method("take_damage"):
28
+ collider.take_damage(25.0)
29
+ ```
30
+
31
+ `force_raycast_update()` matters: the ray otherwise reports last frame's
32
+ state and fast turns produce misses. Cooldown ticks down in `_process` and
33
+ re-arms `can_shoot`.
34
+
35
+ ### Projectile
36
+
37
+ CharacterBody3D moved with `move_and_collide` (not RigidBody - you want
38
+ deterministic flight):
39
+
40
+ ```gdscript
41
+ func _physics_process(delta: float) -> void:
42
+ time_alive += delta
43
+ if time_alive >= lifetime: # 5s despawn, always
44
+ queue_free()
45
+ return
46
+ velocity = direction * speed
47
+ if gravity_affected:
48
+ velocity.y -= 9.8 * delta
49
+ var collision := move_and_collide(velocity * delta)
50
+ if collision:
51
+ var collider := collision.get_collider()
52
+ if collider and collider.has_method("take_damage"):
53
+ collider.take_damage(damage)
54
+ _explode()
55
+ queue_free()
56
+ ```
57
+
58
+ Spawn from the camera, not the weapon model, so shots go where the
59
+ crosshair points:
60
+
61
+ ```gdscript
62
+ proj.global_position = camera.global_position
63
+ proj.direction = -camera.global_transform.basis.z
64
+ get_tree().current_scene.add_child(proj)
65
+ ```
66
+
67
+ ## Weapon system (data-driven)
68
+
69
+ WeaponData is a Resource; one .tres (or in-code instance) per weapon:
70
+
71
+ ```gdscript
72
+ class_name WeaponData
73
+ extends Resource
74
+
75
+ @export var weapon_name: String = "Pistol"
76
+ @export var damage: float = 25.0
77
+ @export var fire_rate: float = 0.15 # seconds between shots
78
+ @export var reload_time: float = 1.5
79
+ @export var magazine_size: int = 12
80
+ @export var max_ammo: int = 120
81
+ @export var is_automatic: bool = false
82
+ @export var is_hitscan: bool = true
83
+ @export var spread: float = 0.0 # radians of random spread
84
+ @export var projectile_scene: PackedScene = null
85
+ @export var weapon_model_scene: PackedScene = null
86
+ ```
87
+
88
+ WeaponManager (Node3D child of Player) owns `weapons: Array[WeaponData]`,
89
+ current index, magazine/reserve ammo, and these signals for the HUD:
90
+ `weapon_changed`, `ammo_changed(current, reserve)`,
91
+ `reload_started(time)`, `reload_finished`.
92
+
93
+ Core rules learned the hard way:
94
+
95
+ - Automatic vs semi-auto is one branch: automatic polls
96
+ `Input.is_action_pressed("shoot")`, semi-auto uses
97
+ `is_action_just_pressed`.
98
+ - Fire path: refuse if `not can_shoot or is_reloading`; if magazine empty,
99
+ auto-start reload instead of firing; decrement ammo and emit
100
+ `ammo_changed` BEFORE resolving the hit.
101
+ - Spread: randomize `shoot_ray.target_position` x/y within +-spread before
102
+ the raycast, then RESET it to (0,0,-100) after - a forgotten reset makes
103
+ every later weapon inherit the spread.
104
+ - Switching: clear WeaponHolder children (`queue_free`), instantiate the
105
+ new `weapon_model_scene`, reset `is_reloading`/`can_shoot`/`shoot_timer`.
106
+ Guard against re-equipping the same index.
107
+ - Reload:
108
+
109
+ ```gdscript
110
+ func _start_reload() -> void:
111
+ if is_reloading or magazine_ammo == current_weapon.magazine_size:
112
+ return
113
+ if reserve_ammo <= 0:
114
+ return
115
+ is_reloading = true
116
+ can_shoot = false
117
+ reload_started.emit(current_weapon.reload_time)
118
+ var tween := create_tween()
119
+ tween.tween_interval(current_weapon.reload_time)
120
+ tween.tween_callback(_finish_reload)
121
+
122
+ func _finish_reload() -> void:
123
+ var available := mini(current_weapon.magazine_size - magazine_ammo, reserve_ammo)
124
+ magazine_ammo += available
125
+ reserve_ammo -= available
126
+ is_reloading = false
127
+ can_shoot = true
128
+ ammo_changed.emit(magazine_ammo, reserve_ammo)
129
+ reload_finished.emit()
130
+ ```
131
+
132
+ The `is_reloading` guard is not optional - without it players stack reload
133
+ timers. Partial reloads take `mini(needed, reserve)`, never a full magazine.
134
+
135
+ Weapon switching by number keys: loop `weapons.size()` checking
136
+ `Input.is_action_just_pressed("weapon_%d" % (i + 1))`.
137
+
138
+ ## Interaction system
139
+
140
+ Duck-typed interface - any node is interactable if it implements:
141
+
142
+ ```gdscript
143
+ func interact(player: Node3D) -> void
144
+ func get_interaction_prompt() -> String
145
+ ```
146
+
147
+ The player polls its InteractionRay every physics frame and caches
148
+ `look_target`. Check the collider AND its parent for the methods (the
149
+ collider is often a StaticBody3D child of the actual object):
150
+
151
+ ```gdscript
152
+ func _update_interaction_target() -> void:
153
+ if interaction_ray.is_colliding():
154
+ var target: Node = interaction_ray.get_collider()
155
+ if target and not target.has_method("get_interaction_prompt"):
156
+ target = target.get_parent()
157
+ if target and target.has_method("get_interaction_prompt"):
158
+ look_target = target
159
+ return
160
+ look_target = null
161
+ ```
162
+
163
+ ### Door pattern
164
+
165
+ Node3D root that rotates; mesh + StaticBody3D child positioned at
166
+ (0.5, 1.4, 0) so the hinge is the parent origin, not the door center.
167
+ `_process` lerps `rotation.y` toward `target_rotation`. Open away from the
168
+ player by checking which side they are on:
169
+
170
+ ```gdscript
171
+ var to_door := global_position - player.global_position
172
+ var local_dir := global_transform.basis.inverse() * to_door
173
+ target_rotation = deg_to_rad(open_angle if local_dir.z > 0 else -open_angle)
174
+ ```
175
+
176
+ Prompt reflects state: "Locked" / "Open" / "Close". The door body's
177
+ collision_layer is 6 (Environment + Interactable) so it both blocks
178
+ movement and answers the interaction ray.
179
+
180
+ ### Pickup pattern
181
+
182
+ RigidBody3D with `freeze = true` (physics off, still hit by rays), idle bob
183
+ (`base_y + sin(bob_time) * 0.05`) and slow rotate, randomized starting
184
+ phase (`bob_time = randf() * TAU`) so a row of pickups does not bob in
185
+ lockstep. `interact()` applies the effect and `queue_free()`s.
186
+
187
+ ## HUD
188
+
189
+ ```
190
+ HUD (CanvasLayer)
191
+ Crosshair (TextureRect, centered 16x16)
192
+ InteractionPrompt (Label, bottom center, "[E] Open")
193
+ AmmoLabel (Label, bottom right, "12 / 120")
194
+ HealthBar (ProgressBar, bottom left)
195
+ DamageOverlay (ColorRect, full rect, red, alpha 0)
196
+ ```
197
+
198
+ Wiring pattern: the HUD finds the player via the "player" group in
199
+ `_process` (retry until found - spawn order is not guaranteed), then
200
+ connects `weapon_manager.ammo_changed`. Interaction prompt shows
201
+ `"[E] " + player.look_target.get_interaction_prompt()` while `look_target`
202
+ is set, hides otherwise.
203
+
204
+ Damage feedback: on hit, set the overlay to `Color(1, 0, 0, 0.3)` and fade
205
+ alpha down at 2.0/s in `_process`. Cheap and effective.
206
+
207
+ ## Order of work
208
+
209
+ 1. Hitscan fire on the base controller (fixed 25 damage, no weapon data).
210
+ 2. Hit effects + damage overlay - shooting must feel real before it is
211
+ configurable.
212
+ 3. WeaponData + WeaponManager, migrate the hardcoded fire into it, add
213
+ ammo/reload + HUD ammo label.
214
+ 4. Second and third weapons (one automatic with spread, one projectile) to
215
+ prove the data path.
216
+ 5. Interaction ray + door + pickup + prompt label.
@@ -0,0 +1,177 @@
1
+ # 3D FPS - Core
2
+
3
+ First-person shooter blueprint: player hierarchy, mouse look, movement feel,
4
+ and level setup. Shooting, weapons, interaction, and HUD live in
5
+ `3d-fps-systems.md`.
6
+
7
+ Rule for Summer Engine builds: scene operations (.tscn) go through the scene
8
+ tools (`summer_add_node` / `summer_set_prop` / `summer_set_resource_property`,
9
+ or one `summer_batch`, always with `scenePath`); script files (.gd) go through
10
+ `summer_write_file` / `summer_replace_text`. Never cross the streams. For a
11
+ production controller with air control and external velocity, see the
12
+ `fps-controller` skill.
13
+
14
+ ## Minimum playable base (build this first)
15
+
16
+ Level with ground + light + WorldEnvironment, the player scene below, mouse
17
+ look + WASD + jump. Prove "walk around and look" before any weapon exists.
18
+
19
+ ## Player hierarchy (non-negotiable)
20
+
21
+ ```
22
+ Player (CharacterBody3D, group "player", collision_mask 6)
23
+ StandingShape (CollisionShape3D, capsule r 0.35 h 1.8, at y 0.9)
24
+ CrouchingShape (CollisionShape3D, capsule r 0.35 h 1.0, at y 0.5, disabled)
25
+ Head (Node3D, y 1.5) # receives PITCH
26
+ Camera3D (fov 70)
27
+ WeaponHolder (Node3D, (0.2, -0.15, -0.4))
28
+ InteractionRay (RayCast3D, target (0,0,-2.5), mask 4)
29
+ ShootRay (RayCast3D, target (0,0,-100), mask 6)
30
+ CeilingCheck (RayCast3D, at y 1, target (0,1,0), mask 2)
31
+ ```
32
+
33
+ Why this exact shape:
34
+
35
+ - Yaw rotates the Player ROOT, pitch rotates Head. Rotating the camera on
36
+ both axes causes gimbal lock and drift.
37
+ - Camera under Head inherits both rotations; WeaponHolder under Camera makes
38
+ weapons track the view exactly.
39
+ - Two collision shapes for crouch, toggled via `disabled`. Resizing one
40
+ shape at runtime glitches physics.
41
+ - Short InteractionRay (2.5m) and long ShootRay (100m) are separate rays with
42
+ separate masks.
43
+
44
+ Collision layers: 1 Player, 2 Environment, 3 Interactable, 4 Enemy. ShootRay
45
+ mask 6 = Environment + Enemy (never the player). InteractionRay mask 4 =
46
+ Interactable only.
47
+
48
+ Input map before any scripts: move_forward/back/left/right, jump (Space),
49
+ sprint (Shift), crouch (C), shoot (LMB), aim (RMB), reload (R), interact (E),
50
+ weapon_1/2/3, pause (Escape).
51
+
52
+ ## Controller script
53
+
54
+ Tuning baseline: walk 5.0, sprint 8.0, crouch 2.5, acceleration 10.0, jump
55
+ 4.5, mouse sensitivity 0.002, FOV 70 default / 80 sprint / 50 aim.
56
+
57
+ ```gdscript
58
+ func _ready() -> void:
59
+ Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
60
+
61
+ func _unhandled_input(event: InputEvent) -> void:
62
+ if event.is_action_pressed("ui_cancel"):
63
+ Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
64
+ return
65
+ if event is InputEventMouseButton and event.pressed \
66
+ and Input.mouse_mode == Input.MOUSE_MODE_VISIBLE:
67
+ Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
68
+ get_viewport().set_input_as_handled()
69
+ return
70
+ if event is InputEventMouseMotion:
71
+ rotate_y(-event.relative.x * mouse_sensitivity)
72
+ head.rotate_x(-event.relative.y * mouse_sensitivity)
73
+ head.rotation.x = clampf(head.rotation.x, deg_to_rad(-85.0), deg_to_rad(85.0))
74
+ ```
75
+
76
+ Mouse capture is mandatory or look does not work; the Escape release +
77
+ click-to-recapture pair is mandatory or the player cannot quit.
78
+
79
+ Movement (in `_physics_process`, never `_process`):
80
+
81
+ ```gdscript
82
+ func _handle_movement(delta: float) -> void:
83
+ var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
84
+ var direction := (transform.basis * Vector3(input_dir.x, 0.0, input_dir.y)).normalized()
85
+ if direction:
86
+ velocity.x = lerpf(velocity.x, direction.x * current_speed, acceleration * delta)
87
+ velocity.z = lerpf(velocity.z, direction.z * current_speed, acceleration * delta)
88
+ else:
89
+ velocity.x = lerpf(velocity.x, 0.0, acceleration * delta)
90
+ velocity.z = lerpf(velocity.z, 0.0, acceleration * delta)
91
+ if is_on_floor() and Input.is_action_just_pressed("jump") and not is_crouching:
92
+ velocity.y = jump_velocity
93
+ ```
94
+
95
+ Gravity from project settings, not a magic number:
96
+ `velocity.y -= ProjectSettings.get_setting("physics/3d/default_gravity") * delta`
97
+ when not on floor. Then `move_and_slide()`.
98
+
99
+ ### Sprint + stamina
100
+
101
+ Sprint only while pressing sprint, moving (velocity > 0.5), not crouching,
102
+ and stamina > 0. Drain 20/s, regen 15/s to 100. Sprinting also pushes FOV to
103
+ 80 (lerp at 5.0/delta), aim pulls to 50 - the FOV lerp is a big part of feel.
104
+
105
+ ### Crouch
106
+
107
+ Toggle: swap which CollisionShape3D is disabled, lerp `head.position.y`
108
+ between 1.5 and 0.8 at 8.0/delta. Standing up first checks
109
+ `ceiling_check.is_colliding()` - refuse to stand under low geometry.
110
+ Crouching cancels sprint and blocks jump.
111
+
112
+ ### Headbob + footsteps
113
+
114
+ ```gdscript
115
+ func _handle_headbob(delta: float) -> void:
116
+ var speed := Vector2(velocity.x, velocity.z).length()
117
+ if speed < 0.5 or not is_on_floor():
118
+ camera.position.y = lerpf(camera.position.y, 0.0, 5.0 * delta)
119
+ camera.position.x = lerpf(camera.position.x, 0.0, 5.0 * delta)
120
+ return
121
+ headbob_time += delta * (14.0 if is_sprinting else 10.0)
122
+ var intensity := 0.05 if is_sprinting else 0.03
123
+ var bob_y := sin(headbob_time) * intensity
124
+ camera.position.y = bob_y
125
+ camera.position.x = sin(headbob_time * 0.5) * intensity * 0.5
126
+ if headbob_last_y >= 0.0 and bob_y < 0.0:
127
+ _play_footstep() # sine crossing zero downward = foot plants
128
+ headbob_last_y = bob_y
129
+ ```
130
+
131
+ The lerp-to-zero branch when stopped is required - without it the camera
132
+ freezes mid-bob. The zero-crossing footstep trigger syncs audio to motion
133
+ for free.
134
+
135
+ ## Level scene
136
+
137
+ The real level comes from real assets, not primitives: route the need through
138
+ the `asset-strategy` skill (`summer_search_assets`, then `summer_import_asset` /
139
+ `summer_import_asset_by_id`), import a ready location or kit that fits, and build
140
+ gameplay inside it; weapons and props should share its style. A PlaneMesh ground
141
+ is only the walk-test blockout and for pieces no asset covers — never the
142
+ shipped level when a real one is available.
143
+
144
+ Blockout minimum: Node3D root + WorldEnvironment + ground (StaticBody3D with
145
+ BoxShape3D collision AND a PlaneMesh visual - a MeshInstance3D alone has no
146
+ collision) + DirectionalLight3D at (-45, 30, 0) with shadows + the player
147
+ instance at (0, 1, 0).
148
+
149
+ Environment presets:
150
+
151
+ - Outdoor: ProceduralSkyMaterial sky, angled DirectionalLight3D with
152
+ shadows, ambient from sky.
153
+ - Indoor: no sky (custom color background), Omni/SpotLights per room, low
154
+ ambient, SSAO on, optional fog.
155
+
156
+ ## Build order
157
+
158
+ 1. Input map + collision layer names.
159
+ 2. Player scene per the hierarchy above (MCP), controller script (files).
160
+ 3. Level: blockout ground, light, environment. Walk test.
161
+ 4. Swap the blockout for an imported location + props (`asset-strategy`).
162
+ 5. Hitscan shooting + one weapon + crosshair (`3d-fps-systems.md`).
163
+ 6. Interaction (door, pickup), HUD, then weapon roster.
164
+
165
+ ## Traps
166
+
167
+ - Mouse look on the Camera directly - split yaw (root) / pitch (Head) or
168
+ you get gimbal lock.
169
+ - Movement in `_process` - variable framerate stutter. CharacterBody3D
170
+ moves in `_physics_process` only.
171
+ - Forgetting `MOUSE_MODE_CAPTURED` (look does nothing) or the Escape
172
+ release (player trapped).
173
+ - Camera at y 0 - you see from your feet. Head height is 1.5-1.7.
174
+ - Level geometry without StaticBody3D collision - the player falls through
175
+ anything that is only a MeshInstance3D.
176
+ - One resized collision shape for crouch instead of two toggled shapes.
177
+ - ShootRay mask including the Player layer - you shoot yourself; mask 6.