summer-engine 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) 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 +120 -0
  8. package/README.md +22 -7
  9. package/assets/audit/scene_audit.gd +4136 -0
  10. package/assets/seeing/seeing_probe.gd +1873 -0
  11. package/dist/cli/commands/setup.js +6 -19
  12. package/dist/cli/commands/skills.js +57 -32
  13. package/dist/core/api-client.d.ts +9 -1
  14. package/dist/core/api-client.js +21 -4
  15. package/dist/core/capabilities/agent-playbook.js +46 -0
  16. package/dist/core/capabilities/audit/accept.d.ts +55 -0
  17. package/dist/core/capabilities/audit/accept.js +147 -0
  18. package/dist/core/capabilities/audit/args.d.ts +95 -0
  19. package/dist/core/capabilities/audit/args.js +89 -0
  20. package/dist/core/capabilities/audit/audit.d.ts +56 -0
  21. package/dist/core/capabilities/audit/audit.js +287 -0
  22. package/dist/core/capabilities/audit/frame.d.ts +14 -0
  23. package/dist/core/capabilities/audit/frame.js +35 -0
  24. package/dist/core/capabilities/audit/gaps.d.ts +96 -0
  25. package/dist/core/capabilities/audit/gaps.js +257 -0
  26. package/dist/core/capabilities/audit/judge.d.ts +162 -0
  27. package/dist/core/capabilities/audit/judge.js +1134 -0
  28. package/dist/core/capabilities/audit/math.d.ts +110 -0
  29. package/dist/core/capabilities/audit/math.js +263 -0
  30. package/dist/core/capabilities/audit/summary.d.ts +74 -0
  31. package/dist/core/capabilities/audit/summary.js +212 -0
  32. package/dist/core/capabilities/board.d.ts +31 -0
  33. package/dist/core/capabilities/board.js +43 -0
  34. package/dist/core/capabilities/camera-view.d.ts +4 -2
  35. package/dist/core/capabilities/camera-view.js +6 -3
  36. package/dist/core/capabilities/connect-signal.d.ts +85 -0
  37. package/dist/core/capabilities/connect-signal.js +285 -0
  38. package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
  39. package/dist/core/capabilities/engine-fallbacks.js +5 -0
  40. package/dist/core/capabilities/engine-ops.d.ts +5 -1
  41. package/dist/core/capabilities/engine-ops.js +0 -3
  42. package/dist/core/capabilities/engine-receipt.d.ts +12 -0
  43. package/dist/core/capabilities/engine-receipt.js +26 -0
  44. package/dist/core/capabilities/file-read.d.ts +64 -0
  45. package/dist/core/capabilities/file-read.js +325 -0
  46. package/dist/core/capabilities/game-task-plan.js +4 -2
  47. package/dist/core/capabilities/grep.d.ts +60 -0
  48. package/dist/core/capabilities/grep.js +190 -0
  49. package/dist/core/capabilities/inspect-node.d.ts +56 -0
  50. package/dist/core/capabilities/inspect-node.js +225 -0
  51. package/dist/core/capabilities/inspect-resource.d.ts +61 -0
  52. package/dist/core/capabilities/inspect-resource.js +376 -0
  53. package/dist/core/capabilities/math3d.d.ts +28 -0
  54. package/dist/core/capabilities/math3d.js +88 -0
  55. package/dist/core/capabilities/motion-generation.d.ts +62 -0
  56. package/dist/core/capabilities/motion-generation.js +138 -0
  57. package/dist/core/capabilities/placement-batch.d.ts +102 -0
  58. package/dist/core/capabilities/placement-batch.js +483 -0
  59. package/dist/core/capabilities/placement-math.d.ts +70 -0
  60. package/dist/core/capabilities/placement-math.js +209 -0
  61. package/dist/core/capabilities/placement-script.d.ts +42 -0
  62. package/dist/core/capabilities/placement-script.js +1096 -0
  63. package/dist/core/capabilities/placement.d.ts +485 -0
  64. package/dist/core/capabilities/placement.js +1731 -0
  65. package/dist/core/capabilities/project-context.d.ts +33 -2
  66. package/dist/core/capabilities/project-context.js +87 -4
  67. package/dist/core/capabilities/replace-node.d.ts +91 -0
  68. package/dist/core/capabilities/replace-node.js +423 -0
  69. package/dist/core/capabilities/runtime-control.d.ts +20 -4
  70. package/dist/core/capabilities/runtime-control.js +67 -2
  71. package/dist/core/capabilities/scene-batch.d.ts +63 -0
  72. package/dist/core/capabilities/scene-batch.js +394 -0
  73. package/dist/core/capabilities/scene-readback.d.ts +36 -0
  74. package/dist/core/capabilities/scene-readback.js +54 -0
  75. package/dist/core/capabilities/seeing/args.d.ts +348 -0
  76. package/dist/core/capabilities/seeing/args.js +178 -0
  77. package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
  78. package/dist/core/capabilities/seeing/candidates.js +282 -0
  79. package/dist/core/capabilities/seeing/math.d.ts +134 -0
  80. package/dist/core/capabilities/seeing/math.js +269 -0
  81. package/dist/core/capabilities/seeing/probe.d.ts +62 -0
  82. package/dist/core/capabilities/seeing/probe.js +188 -0
  83. package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
  84. package/dist/core/capabilities/seeing/scoring.js +639 -0
  85. package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
  86. package/dist/core/capabilities/seeing/seeing.js +1341 -0
  87. package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
  88. package/dist/core/capabilities/seeing/shot-store.js +204 -0
  89. package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
  90. package/dist/core/capabilities/surface-snap-mesh.js +425 -0
  91. package/dist/core/capabilities/surface-snap.d.ts +77 -0
  92. package/dist/core/capabilities/surface-snap.js +461 -0
  93. package/dist/core/capabilities/tool-dispatch.js +231 -72
  94. package/dist/core/capabilities/tscn.d.ts +85 -0
  95. package/dist/core/capabilities/tscn.js +391 -0
  96. package/dist/core/capabilities/world-snapshot.d.ts +58 -0
  97. package/dist/core/capabilities/world-snapshot.js +150 -0
  98. package/dist/core/capability-skew.d.ts +5 -5
  99. package/dist/core/capability-skew.js +14 -6
  100. package/dist/core/headless/worker-engine-client.d.ts +1 -1
  101. package/dist/core/headless/worker-engine-client.js +2 -2
  102. package/dist/core/library-read.d.ts +37 -5
  103. package/dist/core/library-read.js +264 -33
  104. package/dist/core/library-search.d.ts +2 -2
  105. package/dist/core/skills-registry.d.ts +9 -1
  106. package/dist/core/skills-registry.js +25 -2
  107. package/dist/installer/agent-config.d.ts +9 -4
  108. package/dist/installer/agent-config.js +194 -392
  109. package/dist/installer/agent-table.d.ts +119 -0
  110. package/dist/installer/agent-table.js +619 -0
  111. package/dist/installer/setup.js +4 -2
  112. package/dist/installer/skill-locations.d.ts +8 -17
  113. package/dist/installer/skill-locations.js +22 -101
  114. package/dist/installer/version-check.d.ts +5 -1
  115. package/dist/installer/version-check.js +38 -28
  116. package/dist/mcp/server.js +9 -3
  117. package/dist/mcp/tools/asset-tools.js +21 -3
  118. package/dist/mcp/tools/audit-tools.d.ts +23 -0
  119. package/dist/mcp/tools/audit-tools.js +60 -0
  120. package/dist/mcp/tools/debug-tools.js +3 -1
  121. package/dist/mcp/tools/file-tools.js +17 -4
  122. package/dist/mcp/tools/generate-tools.js +74 -66
  123. package/dist/mcp/tools/library-tools.js +2 -0
  124. package/dist/mcp/tools/perception-tools.js +6 -19
  125. package/dist/mcp/tools/placement-tools.d.ts +24 -0
  126. package/dist/mcp/tools/placement-tools.js +91 -0
  127. package/dist/mcp/tools/project-tools.js +18 -15
  128. package/dist/mcp/tools/scene-tools.js +71 -83
  129. package/dist/mcp/tools/seeing-tools.d.ts +24 -0
  130. package/dist/mcp/tools/seeing-tools.js +64 -0
  131. package/dist/mcp/tools/spatial-tools.js +17 -9
  132. package/dist/mcp/tools/visual-tools.js +59 -7
  133. package/gemini-extension.json +26 -4
  134. package/integrations/README.md +48 -19
  135. package/integrations/amp/README.md +12 -0
  136. package/integrations/amp/manifest-target.json +3 -0
  137. package/integrations/antigravity/README.md +12 -0
  138. package/integrations/antigravity/manifest-target.json +3 -0
  139. package/integrations/claude-desktop/README.md +12 -0
  140. package/integrations/claude-desktop/manifest-target.json +3 -0
  141. package/integrations/cline/README.md +8 -9
  142. package/integrations/cline-cli/README.md +12 -0
  143. package/integrations/cline-cli/manifest-target.json +3 -0
  144. package/integrations/copilot-jetbrains/README.md +12 -0
  145. package/integrations/copilot-jetbrains/manifest-target.json +3 -0
  146. package/integrations/crush/README.md +12 -0
  147. package/integrations/crush/manifest-target.json +3 -0
  148. package/integrations/github-copilot/README.md +8 -8
  149. package/integrations/goose/README.md +12 -0
  150. package/integrations/goose/manifest-target.json +3 -0
  151. package/integrations/grok-build/README.md +12 -0
  152. package/integrations/grok-build/manifest-target.json +3 -0
  153. package/integrations/hermes/README.md +12 -0
  154. package/integrations/hermes/manifest-target.json +3 -0
  155. package/integrations/junie/README.md +12 -0
  156. package/integrations/junie/manifest-target.json +3 -0
  157. package/integrations/kilo-code/README.md +6 -10
  158. package/integrations/kimi-code/README.md +12 -0
  159. package/integrations/kimi-code/manifest-target.json +3 -0
  160. package/integrations/kiro/README.md +12 -0
  161. package/integrations/kiro/manifest-target.json +3 -0
  162. package/integrations/lm-studio/README.md +8 -8
  163. package/integrations/mistral-vibe/README.md +12 -0
  164. package/integrations/mistral-vibe/manifest-target.json +3 -0
  165. package/integrations/opencode/README.md +9 -12
  166. package/integrations/qoder/README.md +12 -0
  167. package/integrations/qoder/manifest-target.json +3 -0
  168. package/integrations/qwen-code/README.md +12 -0
  169. package/integrations/qwen-code/manifest-target.json +3 -0
  170. package/integrations/roo-code/README.md +8 -7
  171. package/integrations/rovo-dev/README.md +12 -0
  172. package/integrations/rovo-dev/manifest-target.json +3 -0
  173. package/integrations/trae/README.md +12 -0
  174. package/integrations/trae/manifest-target.json +3 -0
  175. package/integrations/visual-studio/README.md +12 -0
  176. package/integrations/visual-studio/manifest-target.json +3 -0
  177. package/integrations/vscode-copilot/README.md +9 -9
  178. package/integrations/warp/README.md +12 -0
  179. package/integrations/warp/manifest-target.json +3 -0
  180. package/integrations/windsurf/README.md +8 -9
  181. package/integrations/zed/README.md +12 -0
  182. package/integrations/zed/manifest-target.json +3 -0
  183. package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
  184. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  185. package/library/references/summer-folder/resource.yaml +1 -1
  186. package/library/references/summer-folder/summer-folder.md +1 -1
  187. package/library/skills/3d-lighting/SKILL.md +1 -1
  188. package/library/skills/README.md +264 -0
  189. package/library/skills/adaptive-music/SKILL.md +1 -1
  190. package/library/skills/agent-playtesting/SKILL.md +1 -1
  191. package/library/skills/ai-characters-in-games/SKILL.md +220 -0
  192. package/library/skills/ai-characters-in-games/resource.yaml +30 -0
  193. package/library/skills/ambient-bed/SKILL.md +1 -1
  194. package/library/skills/animated-loop/SKILL.md +1 -1
  195. package/library/skills/animation-tree/SKILL.md +1 -1
  196. package/library/skills/art-direction/SKILL.md +1 -1
  197. package/library/skills/asset-strategy/SKILL.md +2 -1
  198. package/library/skills/asset-strategy/resource.yaml +2 -1
  199. package/library/skills/audio-direction/SKILL.md +1 -1
  200. package/library/skills/auto-fire-targeting/SKILL.md +1 -1
  201. package/library/skills/brainstorm-game/SKILL.md +1 -1
  202. package/library/skills/brainstorming/SKILL.md +1 -1
  203. package/library/skills/browse-templates/SKILL.md +3 -3
  204. package/library/skills/browse-templates/resource.yaml +1 -1
  205. package/library/skills/camera-rigs/SKILL.md +128 -0
  206. package/library/skills/camera-rigs/resource.yaml +29 -0
  207. package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
  208. package/library/skills/character-animation-wiring/SKILL.md +1 -1
  209. package/library/skills/character-model/SKILL.md +1 -1
  210. package/library/skills/character-movement/SKILL.md +326 -0
  211. package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
  212. package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
  213. package/library/skills/character-movement/references/movement-3d.md +136 -0
  214. package/library/skills/character-movement/references/platformer-feel.md +83 -0
  215. package/library/skills/character-movement/resource.yaml +40 -0
  216. package/library/skills/character-portrait/SKILL.md +1 -1
  217. package/library/skills/cinematic-cutscene/SKILL.md +1 -1
  218. package/library/skills/combat-basics/SKILL.md +159 -0
  219. package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
  220. package/library/skills/combat-basics/resource.yaml +34 -0
  221. package/library/skills/concept-art/SKILL.md +1 -1
  222. package/library/skills/create-asset-sheet/SKILL.md +1 -1
  223. package/library/skills/debug/SKILL.md +1 -1
  224. package/library/skills/debugging-game-feel/SKILL.md +1 -1
  225. package/library/skills/design-level/SKILL.md +1 -1
  226. package/library/skills/design-mechanic/SKILL.md +1 -1
  227. package/library/skills/design-npc/SKILL.md +1 -1
  228. package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
  229. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
  230. package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
  231. package/library/skills/environment-kit/SKILL.md +1 -1
  232. package/library/skills/export-and-ship/SKILL.md +1 -1
  233. package/library/skills/fabricating-assets/SKILL.md +1 -1
  234. package/library/skills/facial-and-lipsync/SKILL.md +1 -1
  235. package/library/skills/fps-controller/SKILL.md +9 -7
  236. package/library/skills/fps-controller/resource.yaml +4 -1
  237. package/library/skills/game-feel/SKILL.md +1 -1
  238. package/library/skills/gameskill/SKILL.md +1 -1
  239. package/library/skills/gdscript-patterns/SKILL.md +1 -1
  240. package/library/skills/generate-motion/SKILL.md +9 -9
  241. package/library/skills/generate-motion/resource.yaml +2 -1
  242. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
  243. package/library/skills/headless-scripting/SKILL.md +1 -1
  244. package/library/skills/in-game-purchases/SKILL.md +254 -0
  245. package/library/skills/in-game-purchases/resource.yaml +29 -0
  246. package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
  247. package/library/skills/investigating-bugs/SKILL.md +1 -1
  248. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
  249. package/library/skills/make-game/SKILL.md +25 -3
  250. package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
  251. package/library/skills/make-game/references/2d-platformer.md +117 -0
  252. package/library/skills/make-game/references/2d-rpg-content.md +234 -0
  253. package/library/skills/make-game/references/2d-rpg.md +241 -0
  254. package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
  255. package/library/skills/make-game/references/2d-survivors.md +237 -0
  256. package/library/skills/make-game/references/3d-fps-systems.md +216 -0
  257. package/library/skills/make-game/references/3d-fps.md +177 -0
  258. package/library/skills/make-game/references/3d-horror-systems.md +186 -0
  259. package/library/skills/make-game/references/3d-horror.md +201 -0
  260. package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
  261. package/library/skills/make-game/references/3d-third-person.md +215 -0
  262. package/library/skills/make-game/resource.yaml +6 -1
  263. package/library/skills/multiplayer/SKILL.md +150 -0
  264. package/library/skills/multiplayer/resource.yaml +33 -0
  265. package/library/skills/multiplayer-movement/SKILL.md +510 -0
  266. package/library/skills/multiplayer-movement/files.md +501 -0
  267. package/library/skills/multiplayer-movement/resource.yaml +30 -0
  268. package/library/skills/multiplayer-project/SKILL.md +521 -0
  269. package/library/skills/multiplayer-project/files.md +448 -0
  270. package/library/skills/multiplayer-project/resource.yaml +36 -0
  271. package/library/skills/multiplayer-publish/SKILL.md +229 -0
  272. package/library/skills/multiplayer-publish/export-errors.md +89 -0
  273. package/library/skills/multiplayer-publish/resource.yaml +28 -0
  274. package/library/skills/multiplayer-state/SKILL.md +438 -0
  275. package/library/skills/multiplayer-state/files.md +592 -0
  276. package/library/skills/multiplayer-state/resource.yaml +35 -0
  277. package/library/skills/multiplayer-testing/SKILL.md +236 -0
  278. package/library/skills/multiplayer-testing/resource.yaml +39 -0
  279. package/library/skills/music-track/SKILL.md +1 -1
  280. package/library/skills/navigate-summer/SKILL.md +1 -1
  281. package/library/skills/new-project/SKILL.md +1 -1
  282. package/library/skills/organic-model/SKILL.md +1 -1
  283. package/library/skills/pixel-art/SKILL.md +1 -1
  284. package/library/skills/play/SKILL.md +1 -1
  285. package/library/skills/playtesting-a-feature/SKILL.md +1 -1
  286. package/library/skills/procedural-animation/SKILL.md +2 -2
  287. package/library/skills/procedural-animation/resource.yaml +1 -1
  288. package/library/skills/prop-model/SKILL.md +1 -1
  289. package/library/skills/psx-retro-rendering/SKILL.md +1 -1
  290. package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
  291. package/library/skills/remote-deploy/SKILL.md +1 -1
  292. package/library/skills/retarget/SKILL.md +1 -1
  293. package/library/skills/running-in-the-cloud/SKILL.md +1 -1
  294. package/library/skills/save-load/SKILL.md +156 -0
  295. package/library/skills/save-load/resource.yaml +27 -0
  296. package/library/skills/scene-composition/SKILL.md +1 -1
  297. package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
  298. package/library/skills/scene-scripting/SKILL.md +1 -1
  299. package/library/skills/scene-to-level/SKILL.md +1 -1
  300. package/library/skills/skill-create/SKILL.md +1 -1
  301. package/library/skills/skill-improve/SKILL.md +1 -1
  302. package/library/skills/skill-test/SKILL.md +1 -1
  303. package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
  304. package/library/skills/skybox-panorama/SKILL.md +1 -1
  305. package/library/skills/sound-effect/SKILL.md +1 -1
  306. package/library/skills/spatial-placement/SKILL.md +134 -3
  307. package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
  308. package/library/skills/spatial-placement/resource.yaml +16 -2
  309. package/library/skills/sprite-sheet/SKILL.md +1 -1
  310. package/library/skills/summer-analytics/SKILL.md +147 -0
  311. package/library/skills/summer-analytics/resource.yaml +28 -0
  312. package/library/skills/summer-friends/SKILL.md +226 -0
  313. package/library/skills/summer-friends/resource.yaml +28 -0
  314. package/library/skills/summer-leaderboards/SKILL.md +231 -0
  315. package/library/skills/summer-leaderboards/resource.yaml +30 -0
  316. package/library/skills/summer-match-results/SKILL.md +286 -0
  317. package/library/skills/summer-match-results/resource.yaml +32 -0
  318. package/library/skills/summer-matchmaking/SKILL.md +301 -0
  319. package/library/skills/summer-matchmaking/resource.yaml +32 -0
  320. package/library/skills/summer-parties/SKILL.md +253 -0
  321. package/library/skills/summer-parties/resource.yaml +30 -0
  322. package/library/skills/summer-player-data/SKILL.md +322 -0
  323. package/library/skills/summer-player-data/resource.yaml +30 -0
  324. package/library/skills/summer-store/SKILL.md +271 -0
  325. package/library/skills/summer-store/resource.yaml +29 -0
  326. package/library/skills/summer-world-chat/SKILL.md +195 -0
  327. package/library/skills/summer-world-chat/resource.yaml +30 -0
  328. package/library/skills/summer-world-saves/SKILL.md +284 -0
  329. package/library/skills/summer-world-saves/resource.yaml +29 -0
  330. package/library/skills/text-to-motion/SKILL.md +99 -0
  331. package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
  332. package/library/skills/text-to-motion/resource.yaml +41 -0
  333. package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
  334. package/library/skills/tileable-texture/SKILL.md +1 -1
  335. package/library/skills/trailer-shot/SKILL.md +1 -1
  336. package/library/skills/tune-performance/SKILL.md +1 -1
  337. package/library/skills/ui-basics/SKILL.md +10 -1
  338. package/library/skills/ui-graphics/SKILL.md +1 -1
  339. package/library/skills/use-widget-asset/SKILL.md +1 -1
  340. package/library/skills/using-summer/SKILL.md +2 -2
  341. package/library/skills/using-summer/resource.yaml +2 -2
  342. package/library/skills/vehicle-model/SKILL.md +1 -1
  343. package/library/skills/verification-before-completion/SKILL.md +1 -1
  344. package/library/skills/verifying-scenes/SKILL.md +76 -2
  345. package/library/skills/verifying-scenes/resource.yaml +13 -2
  346. package/library/skills/vfx-dissolve/SKILL.md +1 -1
  347. package/library/skills/vfx-fire/SKILL.md +1 -1
  348. package/library/skills/vfx-hit-spark/SKILL.md +1 -1
  349. package/library/skills/vfx-lightning/SKILL.md +1 -1
  350. package/library/skills/vfx-magic-glow/SKILL.md +1 -1
  351. package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
  352. package/library/skills/vfx-smoke/SKILL.md +1 -1
  353. package/library/skills/vfx-water-ripple/SKILL.md +1 -1
  354. package/library/skills/voice-line/SKILL.md +1 -1
  355. package/library/skills/web-and-touch-input/SKILL.md +38 -0
  356. package/library/skills/web-and-touch-input/resource.yaml +28 -0
  357. package/library/skills/web-compatibility-look/SKILL.md +62 -0
  358. package/library/skills/web-compatibility-look/resource.yaml +30 -0
  359. package/library/skills/world-building-3d/SKILL.md +8 -1
  360. package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
  361. package/library/skills/world-building-3d/resource.yaml +2 -1
  362. package/library/skills/writing-plans/SKILL.md +1 -1
  363. package/library/skills/writing-skills/SKILL.md +1 -1
  364. package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
  365. package/library/tools/attach-to-surface/resource.yaml +142 -0
  366. package/library/tools/batch/resource.yaml +9 -3
  367. package/library/tools/connect-ports/resource.yaml +120 -0
  368. package/library/tools/connect-signal/resource.yaml +3 -2
  369. package/library/tools/debug-views/resource.yaml +94 -0
  370. package/library/tools/frame-nodes/resource.yaml +104 -0
  371. package/library/tools/frame-shot/resource.yaml +134 -0
  372. package/library/tools/generate-motion/resource.yaml +44 -9
  373. package/library/tools/get-board/resource.yaml +50 -0
  374. package/library/tools/get-project-context/resource.yaml +13 -2
  375. package/library/tools/grep/resource.yaml +77 -0
  376. package/library/tools/inspect-asset/resource.yaml +71 -0
  377. package/library/tools/inspect-node/resource.yaml +9 -2
  378. package/library/tools/inspect-resource/resource.yaml +16 -5
  379. package/library/tools/instantiate-scene/resource.yaml +27 -2
  380. package/library/tools/measure/resource.yaml +96 -0
  381. package/library/tools/place-adjacent/resource.yaml +113 -0
  382. package/library/tools/play/resource.yaml +15 -2
  383. package/library/tools/raycast/resource.yaml +101 -0
  384. package/library/tools/read-file/resource.yaml +36 -2
  385. package/library/tools/read-library/resource.yaml +2 -2
  386. package/library/tools/repeat-along/resource.yaml +129 -0
  387. package/library/tools/replace-node/resource.yaml +11 -5
  388. package/library/tools/scene-audit/resource.yaml +144 -0
  389. package/library/tools/screenshot/resource.yaml +13 -2
  390. package/library/tools/search-assets/resource.yaml +18 -0
  391. package/library/tools/shot-sheet/resource.yaml +110 -0
  392. package/library/tools/snap-to-surface/resource.yaml +4 -1
  393. package/library/tools/world-snapshot/resource.yaml +24 -3
  394. package/library/tools/zoom/resource.yaml +105 -0
  395. package/package.json +3 -3
  396. package/registry/generated/aliases.json +17 -3
  397. package/registry/generated/counts.json +3 -3
  398. package/registry/generated/gemini-extension.json +26 -4
  399. package/registry/generated/index.json +2154 -469
  400. package/registry/generated/marketplace.claude.json +2 -2
  401. package/registry/generated/plugin.claude.json +27 -5
  402. package/registry/generated/plugin.codex.json +28 -6
  403. package/registry/generated/plugin.cursor.json +27 -5
  404. package/registry/generated/plugin.factory.json +27 -5
  405. package/registry/generated/skills-index.md +264 -0
  406. package/registry/generated/skills-registry.json +901 -206
  407. package/registry/generated/templates-registry.json +2 -2
  408. package/library/skills/host-authoritative-state/SKILL.md +0 -332
  409. package/library/skills/host-authoritative-state/resource.yaml +0 -28
  410. package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
  411. package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
  412. package/library/skills/setup-multiplayer/SKILL.md +0 -336
  413. package/library/skills/setup-multiplayer/resource.yaml +0 -28
@@ -0,0 +1,201 @@
1
+ # 2D Survivors - Progression
2
+
3
+ The systems layer on top of `2d-survivors.md`: enemy roster, wave-driven
4
+ spawning, elites, level-up upgrades, the between-wave shop, weapon evolution,
5
+ and characters. Build these only after the core loop (move + auto-fire + chase)
6
+ runs.
7
+
8
+ ## Enemy roster (one script, many behaviors)
9
+
10
+ All types share `enemy_base.gd` (Area2D, "enemies" group), configured through
11
+ `setup(type, wave)` - no per-type scenes. HP/speed scale with wave number:
12
+
13
+ | Type | Behavior | Health | Speed | Special |
14
+ |---|---|---|---|---|
15
+ | Walker | chase + wobble | 15 + wave*8 | 55 + wave*2 | standard horde unit |
16
+ | Runner | fast chase | 8 + wave*4 | 110 + wave*3 | 0.7x scale |
17
+ | Tank | slow, big | 50 + wave*15 | 35 + wave*1 | 1.5x scale, 3 XP |
18
+ | Boss | massive | 500 + wave*80 | 45 | 2.8x scale, 25 XP |
19
+ | Ranged | keeps distance | 12 + wave*5 | 50 + wave*1.5 | fires projectiles |
20
+ | Charger | telegraph + dash | 20 + wave*7 | 50 + wave*1.5 | 550px/s burst |
21
+ | Spawner | stays back | 80 + wave*20 | 30 | spawns 2-3 minions |
22
+
23
+ Behavior dispatch is a `match behavior:` in `_physics_process`. Give every
24
+ enemy a randomized wobble (`_wobble_phase = randf() * TAU`, per-enemy speed and
25
+ amplitude) so hordes look organic instead of converging in a straight line.
26
+
27
+ Charger is a 4-state machine: WALK -> TELEGRAPH (0.45s, within 320px trigger
28
+ range) -> CHARGE (0.4s at 550px/s in the locked direction) -> RECOVER (0.3s),
29
+ then 3.5s cooldown. The telegraph is what makes it fair - flash/scale the sprite
30
+ during it.
31
+
32
+ Knockback: on hit, set `_knockback_vel = (pos - source).normalized() * 180`,
33
+ decay with `move_toward(Vector2.ZERO, 900 * delta)` and add to movement.
34
+
35
+ ### Hit feedback and death (non-negotiable)
36
+
37
+ `take_damage(amount, source_pos, is_crit)` must: flash the sprite, spawn a
38
+ floating damage number (bigger + colored on crit), spawn directional particles,
39
+ apply knockback, show the health bar. Death: `GameManager.add_kill()`,
40
+ `add_xp(xp_value)`, drop gold (rare health orb), particle burst, then disable
41
+ before animating out:
42
+
43
+ ```gdscript
44
+ set_physics_process(false)
45
+ set_deferred("monitoring", false)
46
+ set_deferred("monitorable", false)
47
+ var tween := create_tween().set_parallel(true)
48
+ tween.tween_property(self, "scale", scale * 1.5, 0.1)
49
+ tween.tween_property(self, "modulate:a", 0.0, 0.1)
50
+ tween.chain().tween_callback(queue_free)
51
+ ```
52
+
53
+ The `set_deferred` pair matters: freeing an Area2D mid-physics-callback without
54
+ disabling monitoring first throws errors.
55
+
56
+ ## Spawner and wave data
57
+
58
+ The spawner reads a per-wave config and spawns groups on an interval, always
59
+ off-screen (650-900px from the player, clamped to arena bounds):
60
+
61
+ ```gdscript
62
+ func _random_spawn_pos() -> Vector2:
63
+ var angle := randf() * TAU
64
+ var dist := randf_range(650.0, 900.0)
65
+ var pos := GameManager.player_node.global_position + Vector2.from_angle(angle) * dist
66
+ pos.x = clampf(pos.x, -ARENA_HALF_SIZE, ARENA_HALF_SIZE)
67
+ pos.y = clampf(pos.y, -ARENA_HALF_SIZE, ARENA_HALF_SIZE)
68
+ return pos
69
+ ```
70
+
71
+ Each WaveConfig: `enemy_weights: Dictionary`, `spawn_interval`, `group_size`,
72
+ `max_alive`, `elite_chance`, `boss: bool`, `hazards: int`, `duration`. Type
73
+ selection is weighted random over the dictionary. Hand-tune 20 waves; the ramp
74
+ that matters:
75
+
76
+ | Wave | Types | Interval | Group | Max alive | Elite % | Boss | Duration |
77
+ |---|---|---|---|---|---|---|---|
78
+ | 1 | walker | 2.5s | 2 | 25 | 0% | no | 20s |
79
+ | 3 | +runner | 2.0s | 4 | 45 | 0% | no | 20s |
80
+ | 5 | +charger | 1.7s | 5 | 65 | 3% | no | 22s |
81
+ | 8 | +tank +ranged | 1.3s | 8 | 100 | 6% | no | 22s |
82
+ | 10 | all 6 | 1.1s | 10 | 120 | 8% | YES | 30s |
83
+ | 15 | tank heavy | 0.75s | 15 | 190 | 13% | no | 28s |
84
+ | 20 | max density | 0.45s | 22 | 250 | 20% | YES | 30s |
85
+
86
+ Do NOT scale difficulty by HP alone. The ramp is spawn rate + group size +
87
+ max alive + type diversity + elite chance + new mechanics at fixed waves.
88
+ Bosses spawn 3s into their combat phase via a scene-tree timer.
89
+
90
+ Arena hazards from wave 3+: spike traps (static) and fire pools (drift at
91
+ 15-35px/s). Tick damage every 0.6s (6 + wave*1.5) to enemies in radius, half
92
+ damage to the player - positioning pressure for both sides.
93
+
94
+ ## Elites
95
+
96
+ `elite_system.gd` is a static RefCounted helper applied at spawn time with
97
+ `elite_chance` probability. One of four modifiers, 3x XP:
98
+
99
+ - FAST: speed x1.8, HP x1.3, lightened color
100
+ - TANKY: HP x3, scale x1.3, contact damage x1.5, darkened
101
+ - SPLITTING: HP x1.5, green; on death spawns 2 walker copies at 0.6x scale,
102
+ 0.3x HP (use `call_deferred("add_child", copy)` - you are inside a physics
103
+ callback)
104
+ - VAMPIRIC: HP x2; heals 8% max HP every 3s
105
+
106
+ Give elites a visible pulse tween so players can prioritize them.
107
+
108
+ ## Two-track progression
109
+
110
+ Track 1 - mid-wave level-ups: on level-up the tree pauses, 3 random stat
111
+ upgrades from the pool are offered, each stackable to level 3. Also vacuum all
112
+ pickups (`attract()` on the "pickups" group) as a reward beat.
113
+
114
+ | Upgrade | Per level | Stat touched |
115
+ |---|---|---|
116
+ | Might | +15% damage | player_damage_mult |
117
+ | Haste | +12% attack speed | player_attack_speed_mult |
118
+ | Swiftness | +20 speed | player_speed |
119
+ | Vitality | +15 max HP (and heal 15) | player_max_health |
120
+ | Magnetism | +20 pickup range | player_pickup_range |
121
+ | Armor | +1 flat reduction | player_armor |
122
+ | Regeneration | +0.5 HP/s | player_regen |
123
+ | Critical Eye | +5% crit | player_crit_chance |
124
+
125
+ `get_random_choices(3)` filters out maxed upgrades, shuffles, takes 3.
126
+ `apply_upgrade` bumps level and writes the stat straight onto GameManager.
127
+ Armor applies as `maxi(1, amount - player_armor)` - always at least 1 damage.
128
+
129
+ Track 2 - between-wave shop: 4 random items, gold-priced, 4 tiers (Common /
130
+ Uncommon / Rare / Legendary). Tier odds shift with wave - by wave 16+:
131
+ 15% Legendary / 30% Rare / 35% Uncommon / 20% Common; waves 1-5 have no
132
+ Legendary at all. Prices scale `cost_base * (1.0 + wave * 0.12)`; reroll costs
133
+ `2 + reroll_count + wave / 5` (escalates within a single shop visit).
134
+
135
+ Items are either stat dictionaries applied to GameManager
136
+ (`{"player_armor": 2}`) or weapon grants that call
137
+ `player.add_or_upgrade_weapon(id)`. The interesting ones trade off:
138
+
139
+ ```gdscript
140
+ _add("glass_cannon", "Glass Cannon", 2, 40,
141
+ {"player_damage_mult": 0.35, "player_max_health": -25}, ["offense"])
142
+ _add("berserker_mask", "Berserker Mask", 2, 45,
143
+ {"player_damage_mult": 0.25, "player_attack_speed_mult": 0.20,
144
+ "player_armor": -3}, ["offense"])
145
+ ```
146
+
147
+ Do not skip the shop. Without a between-wave decision point the game is just
148
+ "survive longer"; trade-off items are what create builds.
149
+
150
+ ## Weapon evolution
151
+
152
+ A base weapon evolves when the player owns it AND a specific catalyst upgrade
153
+ is maxed (level 3). Check after every upgrade pick and shop purchase; emit
154
+ `evolution_available` once per recipe:
155
+
156
+ | Evolved | Base weapon | Catalyst | Effect |
157
+ |---|---|---|---|
158
+ | Piercing Barrage | Projectile | Multi Shot | pierces 4+, faster bullets |
159
+ | Radiant Barrier | Orbital | Armor | 4 large orbitals + damage pulses |
160
+ | Supernova | Holy Nova | Might | bigger AoE + burning ground DOT |
161
+
162
+ Evolving replaces the node: `queue_free()` the old weapon, erase from the
163
+ player's `_weapons` dict, instantiate the evolved script, screen flash + zoom
164
+ pulse. DOT zones (Supernova) are a Polygon2D + tween loop: N tick callbacks at
165
+ 0.5s intervals doing a group distance scan, then fade and free. Guard every
166
+ tick with `is_instance_valid` - the zone can outlive enemies and vice versa.
167
+
168
+ ## Characters
169
+
170
+ Four picks before the run, each a starting weapon + stat override dictionary:
171
+
172
+ - Sentinel: projectile, no overrides (baseline)
173
+ - Berserker: projectile, damage_mult 1.4, speed 240, max HP 80
174
+ - Warden: orbital, max HP 150, armor 3, attack_speed 0.85
175
+ - Arcanist: nova, attack_speed 1.25, damage_mult 1.15, max HP 70
176
+
177
+ `GameManager.reset_run()` resets all stats to base, then applies
178
+ `selected_character.stat_overrides` via `set(key, value)` and fills health to
179
+ max. Character select is just 4 cards that set `selected_character` and load
180
+ the arena.
181
+
182
+ ## UI stack (all procedural, all CanvasLayer, all process_mode ALWAYS)
183
+
184
+ - HUD: health + dash bars top-left, wave counter + timer top-center,
185
+ gold/kills/level top-right, full-width 6px XP bar at the bottom.
186
+ - LevelUpUI (layer 20): dark overlay, 3 cards, gamepad focus.
187
+ - ShopUI (layer 25): stats panel left, 4 tier-colored item cards, reroll +
188
+ next-wave buttons, "SOLD" placeholders, live stat refresh after purchase.
189
+ - PauseMenu / GameOverUI (layer 40): game over shows victory or death plus
190
+ time / kills / level / wave, retry + menu.
191
+
192
+ Retry = `get_tree().paused = false` THEN `reload_current_scene()`. Forgetting
193
+ the unpause is the classic frozen-retry bug.
194
+
195
+ ## Audio integration points
196
+
197
+ Pooled AudioManager (16 global + 16 positional players, round-robin; all play
198
+ methods null-safe so missing streams never crash). Pitch variance 5% global /
199
+ 10% positional prevents machine-gun repetition. Hook: enemy hit + death
200
+ (positional), pickup collect, level-up, shop purchase, weapon fire (-6dB, it
201
+ fires constantly), music on combat start with fade.
@@ -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.