spawnpoint-studio 0.1.1__tar.gz → 0.3.0__tar.gz

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 (165) hide show
  1. {spawnpoint_studio-0.1.1/compiler/spawnpoint_studio.egg-info → spawnpoint_studio-0.3.0}/PKG-INFO +60 -22
  2. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/README.md +59 -21
  3. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/analyze.py +63 -7
  4. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/build.py +5 -2
  5. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cli.py +26 -5
  6. spawnpoint_studio-0.3.0/compiler/spawnpoint/demo.py +274 -0
  7. spawnpoint_studio-0.3.0/compiler/spawnpoint/demo_touch.js +118 -0
  8. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/gif.py +10 -7
  9. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/graph.py +8 -0
  10. spawnpoint_studio-0.3.0/compiler/spawnpoint/llm_server.py +123 -0
  11. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/report.py +4 -1
  12. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/runner.py +16 -1
  13. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/tuner.py +43 -14
  14. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/project.py +3 -1
  15. spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/cartoon.py +477 -0
  16. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/generate.py +206 -13
  17. spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/motion.py +72 -0
  18. spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/palettes.py +111 -0
  19. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/resolve.py +17 -4
  20. spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/scenery.py +471 -0
  21. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/spriteset.py +7 -5
  22. spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/tiles.py +362 -0
  23. spawnpoint_studio-0.3.0/compiler/spawnpoint/studio/assemble.py +1127 -0
  24. spawnpoint_studio-0.3.0/compiler/spawnpoint/studio/brief.py +309 -0
  25. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/edits.py +28 -0
  26. spawnpoint_studio-0.3.0/compiler/spawnpoint/studio/genres.py +248 -0
  27. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/levelgen.py +99 -0
  28. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/llm.py +32 -7
  29. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/orchestrator.py +31 -15
  30. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/prompts.py +50 -4
  31. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/web.py +2 -2
  32. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/webcheck.py +7 -3
  33. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio_cli.py +10 -1
  34. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0/compiler/spawnpoint_studio.egg-info}/PKG-INFO +60 -22
  35. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/SOURCES.txt +34 -0
  36. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/conftest.py +7 -0
  37. spawnpoint_studio-0.3.0/compiler/tests/test_demo.py +80 -0
  38. spawnpoint_studio-0.3.0/compiler/tests/test_genres.py +207 -0
  39. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_live_llm.py +5 -0
  40. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_playtest.py +24 -7
  41. spawnpoint_studio-0.3.0/compiler/tests/test_sprites.py +537 -0
  42. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_studio.py +157 -0
  43. spawnpoint_studio-0.3.0/examples/castle_defense.json +341 -0
  44. spawnpoint_studio-0.3.0/examples/dino_eggs.json +99 -0
  45. spawnpoint_studio-0.3.0/examples/knight_quest.json +348 -0
  46. spawnpoint_studio-0.3.0/examples/night_survivor.json +448 -0
  47. spawnpoint_studio-0.3.0/examples/ninja_rescue.json +351 -0
  48. spawnpoint_studio-0.3.0/examples/star_run.json +444 -0
  49. spawnpoint_studio-0.3.0/examples/sunny_farm.json +223 -0
  50. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/animator_component.gd +18 -2
  51. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/base_component.gd +31 -0
  52. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/bubble_board_component.gd +427 -0
  53. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/crop_pest_component.gd +77 -0
  54. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/experience_component.gd +53 -0
  55. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/farm_plot_component.gd +159 -0
  56. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/farmer_component.gd +158 -0
  57. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/flight_path_component.gd +101 -0
  58. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/ground_patch_component.gd +37 -0
  59. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/health_bar_component.gd +41 -0
  60. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/juice_component.gd +36 -2
  61. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/level_up_menu_component.gd +225 -0
  62. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/market_component.gd +52 -0
  63. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/path_follower_component.gd +95 -0
  64. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/shmup_ship_component.gd +65 -0
  65. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/spawner_component.gd +23 -1
  66. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/tower_brain_component.gd +39 -0
  67. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/tower_builder_component.gd +105 -0
  68. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/tower_slot_component.gd +47 -0
  69. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/wave_spawner_component.gd +106 -0
  70. spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/xp_orb_component.gd +67 -0
  71. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/manifest.json +1226 -62
  72. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/camera_rig.gd +3 -0
  73. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/entity_core.gd +2 -0
  74. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/fx.gd +95 -0
  75. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/hud.gd +52 -2
  76. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/level.gd +3 -0
  77. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/world.gd +43 -0
  78. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/build.gd +256 -11
  79. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/playtest.gd +57 -12
  80. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/playtest_nav.gd +3 -1
  81. spawnpoint_studio-0.3.0/godot/addons/spawn_point/tools/record.gd +233 -0
  82. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/pyproject.toml +2 -2
  83. spawnpoint_studio-0.1.1/compiler/spawnpoint/llm_server.py +0 -77
  84. spawnpoint_studio-0.1.1/compiler/spawnpoint/sprites/tiles.py +0 -102
  85. spawnpoint_studio-0.1.1/compiler/spawnpoint/studio/assemble.py +0 -528
  86. spawnpoint_studio-0.1.1/compiler/spawnpoint/studio/brief.py +0 -197
  87. spawnpoint_studio-0.1.1/compiler/tests/test_sprites.py +0 -261
  88. spawnpoint_studio-0.1.1/godot/addons/spawn_point/tools/record.gd +0 -89
  89. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/LICENSE +0 -0
  90. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/MANIFEST.in +0 -0
  91. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/__init__.py +0 -0
  92. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/__init__.py +0 -0
  93. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/server.py +0 -0
  94. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/static/app.css +0 -0
  95. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/static/app.js +0 -0
  96. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/static/index.html +0 -0
  97. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/diagnostics.py +0 -0
  98. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/expr.py +0 -0
  99. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/godot.py +0 -0
  100. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/level.py +0 -0
  101. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/manifest.py +0 -0
  102. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/__init__.py +0 -0
  103. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/py.typed +0 -0
  104. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/rules.py +0 -0
  105. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sfx.py +0 -0
  106. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/__init__.py +0 -0
  107. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/ai.py +0 -0
  108. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/contour.py +0 -0
  109. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/importer.py +0 -0
  110. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/__init__.py +0 -0
  111. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/creative.py +0 -0
  112. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/dataset.py +0 -0
  113. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/dependency_links.txt +0 -0
  114. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/entry_points.txt +0 -0
  115. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/requires.txt +0 -0
  116. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/top_level.txt +0 -0
  117. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_ai_sprites.py +0 -0
  118. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_analyze.py +0 -0
  119. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_cockpit.py +0 -0
  120. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_e2e.py +0 -0
  121. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_expr.py +0 -0
  122. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_gif.py +0 -0
  123. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_godot_library.py +0 -0
  124. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_graph.py +0 -0
  125. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_level.py +0 -0
  126. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_manifest.py +0 -0
  127. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_rules.py +0 -0
  128. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_sfx.py +0 -0
  129. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_web.py +0 -0
  130. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/coin_hopper.json +0 -0
  131. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/neon_arena.json +0 -0
  132. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/pixel_arena.json +0 -0
  133. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/pixel_hopper.json +0 -0
  134. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/aim_component.gd +0 -0
  135. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/chase_ai_component.gd +0 -0
  136. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/collectible_component.gd +0 -0
  137. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/gate_component.gd +0 -0
  138. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/health_component.gd +0 -0
  139. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/hitbox_component.gd +0 -0
  140. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/hurtbox_component.gd +0 -0
  141. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/patrol_ai_component.gd +0 -0
  142. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/platformer_movement_component.gd +0 -0
  143. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/player_input_component.gd +0 -0
  144. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/projectile_component.gd +0 -0
  145. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/stomp_component.gd +0 -0
  146. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/top_down_movement_component.gd +0 -0
  147. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/trigger_zone_component.gd +0 -0
  148. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/weapon_component.gd +0 -0
  149. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/cockpit.gd +0 -0
  150. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/game_state.gd +0 -0
  151. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/sfx.gd +0 -0
  152. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/sp.gd +0 -0
  153. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/shaders/flash.gdshader +0 -0
  154. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/shaders/floor_grid.gdshader +0 -0
  155. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/build.tscn +0 -0
  156. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/capture.gd +0 -0
  157. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/capture.tscn +0 -0
  158. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/dump_manifest.gd +0 -0
  159. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/dump_manifest.tscn +0 -0
  160. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/playtest.tscn +0 -0
  161. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/record.tscn +0 -0
  162. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/smoke.gd +0 -0
  163. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/smoke.tscn +0 -0
  164. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/setup.cfg +0 -0
  165. {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spawnpoint-studio
3
- Version: 0.1.1
3
+ Version: 0.3.0
4
4
  Summary: Turn a sentence into a juicy, playable Godot 4 game — locally, on a consumer GPU.
5
5
  Author: smoky1496
6
6
  License: MIT
@@ -29,15 +29,26 @@ Dynamic: license-file
29
29
  [![CI](https://github.com/leluong141996-dev/Spawn-Point/actions/workflows/ci.yml/badge.svg)](https://github.com/leluong141996-dev/Spawn-Point/actions/workflows/ci.yml)
30
30
  [![PyPI](https://img.shields.io/pypi/v/spawnpoint-studio)](https://pypi.org/project/spawnpoint-studio/)
31
31
  [![Model](https://img.shields.io/badge/%F0%9F%A4%97%20model-SpawnPoint--Coder--7B-yellow)](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-AWQ)
32
+ [![Play the demos](https://img.shields.io/badge/%E2%96%B6%20play-demos%20in%20your%20browser-ff004d)](https://leluong141996-dev.github.io/Spawn-Point/)
32
33
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
33
34
 
34
35
  <p align="center">
35
- <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-hopper.gif" width="48%" alt="Pixel Hopper — generated platformer">
36
- <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-arena.gif" width="48%" alt="Pixel Arena — generated top-down shooter">
36
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/knight-quest.gif" width="48%" alt="Knight's Quest: a cartoon-style sword platformer">
37
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/castle-defense.gif" width="48%" alt="Castle Defense: a tower defense game">
38
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/ninja-rescue.gif" width="48%" alt="Ninja Rescue: a platformer where a ninja frees his mother">
39
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/dino-eggs.gif" width="48%" alt="Dino Eggs: a bubble shooter with dinosaur eggs">
40
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/night-survivor.gif" width="48%" alt="Night Survivor: auto-fire, gems, level-ups">
41
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-hopper.gif" width="48%" alt="Pixel Hopper: a Mario-style platformer">
42
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/star-run.gif" width="48%" alt="Star Run: a side-scrolling shooter with power-ups and a boss">
37
43
  </p>
38
- <p align="center"><sub>Both games are built from <code>examples/pixel_hopper.json</code> and
39
- <code>examples/pixel_arena.json</code>: generated sprites, collision traced from pixels, juice on by
40
- default. Recorded with <code>spawnpoint gif</code> (autopilot).</sub></p>
44
+ <p align="center"><sub>Seven of the demo games, each one a small JSON graph in <code>examples/</code> built from the
45
+ same component library: generated cartoon or pixel art, collision traced from the sprites, juice on by default.
46
+ Recorded with <code>spawnpoint gif</code> (autopilot).</sub></p>
47
+
48
+ <p align="center"><b><a href="https://leluong141996-dev.github.io/Spawn-Point/">▶ Play the demos in your browser</a></b>: a Mario-style platformer, a cartoon
49
+ knight with a sword, tower defense, a ninja rescue, a dinosaur-egg bubble shooter, a survivor with level-ups, a farming
50
+ game, an arena shooter and a side-scrolling space shooter. No install needed, and
51
+ they work on phones too (touch controls).</p>
41
52
 
42
53
  Spawn Point is an open-source, autonomous game studio that runs on your own machine. Instead of asking an LLM to type
43
54
  hundreds of lines of GDScript (slow, expensive, and easy to break with Godot 3/4 mix-ups), the LLM
@@ -62,16 +73,16 @@ graph.json ──► validate (Python) ──► build scenes (Godot headless)
62
73
  ## Install
63
74
 
64
75
  Requirements: Python ≥ 3.10, [Godot 4.6](https://godotengine.org/download) on your `PATH` (`godot4` or
65
- `godot`, or set `GODOT_BIN`), and for the LLM features either a 12 GB NVIDIA GPU with Docker (local model)
66
- or any OpenAI-compatible API.
76
+ `godot`, or set `GODOT_BIN`), and for the LLM features one of: a 12 GB NVIDIA GPU with Docker (vLLM, fastest),
77
+ [Ollama](https://ollama.com/download) on any machine (Mac, AMD, or CPU), or any OpenAI-compatible API.
67
78
 
68
79
  ```bash
69
80
  pip install spawnpoint-studio
70
- spawnpoint llm up # serves SpawnPoint-Coder-7B locally (or set SPAWNPOINT_LLM_* for an API)
81
+ spawnpoint llm up # SpawnPoint-Coder-7B: vLLM on NVIDIA, otherwise Ollama (or set SPAWNPOINT_LLM_*)
71
82
  spawnpoint create "a ninja platformer where you collect stars"
72
83
  ```
73
84
 
74
- No GPU? Try the hand-written examples, no LLM needed:
85
+ No GPU? [Play the demos in your browser](https://leluong141996-dev.github.io/Spawn-Point/), or build the hand-written examples locally, no LLM needed:
75
86
 
76
87
  ```bash
77
88
  spawnpoint examples copy pixel_hopper && spawnpoint build pixel_hopper.json --run
@@ -102,7 +113,7 @@ Standard library only on the server, one HTML/JS page on the client, Server-Sent
102
113
  ## Make a game from a sentence
103
114
 
104
115
  ```bash
105
- scripts/llm-up.sh # SpawnPoint-Coder-7B on vLLM (~11 GB VRAM) — or any OpenAI-compatible API
116
+ spawnpoint llm up # SpawnPoint-Coder-7B: vLLM (~11 GB VRAM) or Ollama — or any OpenAI-compatible API
106
117
  spawnpoint create "a ninja platformer where you collect stars"
107
118
  ```
108
119
 
@@ -145,23 +156,42 @@ Only what you asked for changes, and the level layout is kept. Prompts can be in
145
156
  **The model.** [SpawnPoint-Coder-7B](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-AWQ) is a 7B model
146
157
  fine-tuned to generate Godot 4 games through this pipeline: it writes the game design, tweaks and entity
147
158
  edits as strict JSON, and the compiler turns that into Godot scenes and GDScript. It runs on a single 12 GB
148
- GPU (AWQ 4-bit). v2 on 30 human-written game ideas (English and Vietnamese): 87% fully right (v1: 73%),
149
- 97% of checks passed; on held-out tasks: briefs 88% exact (v1: 36%), Vietnamese → English 97% (v1: 52%),
150
- tweaks 98%, entity edits 91%. It is tuned for 2D platformers and 2D top-down games only; other genres
151
- are no better than a general 7B coder. `scripts/llm-up.sh` downloads and serves it.
159
+ GPU (AWQ 4-bit). v5 adds **side-scrolling shooters** (flight patterns, power-ups, a boss), reads **Vietnamese prompts directly** and writes a small per-genre format instead of one big form. The downloadable AWQ build gets 75% of a sealed set of 16 human-written prompts for shooters and the v4 fixes fully right, and 77% of 30 human-written game ideas in English and Vietnamese (v4 scored a bit higher on its older sets; the model card lists every number and where v5 is behind). The GGUF Q4_K_M build scores 73% on those 30. Older models still work with `SPAWNPOINT_BRIEF_FORMAT=v4`. It is tuned for the 2D game kinds Spawn Point builds; other genres are no better than a general 7B coder. `spawnpoint llm up` downloads and serves it: the AWQ build on vLLM when
160
+ Docker and an NVIDIA GPU are present, otherwise the [GGUF build](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-GGUF)
161
+ (Q4_K_M, ~4.7 GB) in Ollama. Force one with `--backend vllm|ollama`.
152
162
 
153
- LLM settings: `SPAWNPOINT_LLM_BASE_URL` (default `http://localhost:8000/v1`),
154
- `SPAWNPOINT_LLM_MODEL` (default: the server's first model), `SPAWNPOINT_LLM_API_KEY`.
163
+ LLM settings: `SPAWNPOINT_LLM_BASE_URL` (default: vLLM at `http://localhost:8000/v1`, or Ollama at
164
+ `http://localhost:11434/v1` when only Ollama answers), `SPAWNPOINT_LLM_MODEL` (default: SpawnPoint-Coder if the
165
+ server has it, else its first model), `SPAWNPOINT_LLM_API_KEY`.
155
166
  Web builds need the Godot **web export templates** (Editor → Manage Export Templates, or unpack
156
167
  `web_*.zip` from the official `.tpz` into `~/.local/share/godot/export_templates/<version>/`);
157
168
  use `--no-web` to skip and play natively with `spawnpoint run`.
158
169
 
159
170
  ## Sprites: generated or your own
160
171
 
161
- Layer 2 replaces placeholder shapes with **procedural pixel-art sprites** — no assets, no GPU. The
172
+ Layer 2 replaces placeholder shapes with **procedural sprites** — no assets, no GPU — in one of two art styles:
173
+ **pixel art** (the default) or **cartoon** (`"art_style": "cartoon"` in the graph settings, or "cartoon style" in
174
+ the prompt): smooth shaded sprites with a sticker outline, rounded tiles, and a parallax sky with hills, clouds and
175
+ trees. The
162
176
  kind follows the entity (`Goblin` → humanoid, `FireBat` → flyer, `LaserBot` → robot, `Crystal` → gem…)
163
- and details follow the name (`Ninja` → mask, `Fox` → ears and tail, `Spider` → legs). Every sprite has
164
- idle / run / jump / fall / hurt animations driven automatically by an `Animator` component.
177
+ and details follow the name (`Ninja` → mask, `Fox` → ears and tail, `Spider` → legs, a sword-wielder holds a sword).
178
+ Every sprite has idle / run / jump / fall / hurt animations driven automatically by an `Animator` component, plus
179
+ `attack` (played each time its weapon fires) and `die` (a death sequence that plays on after the entity is gone) for
180
+ anything that fights or can be hurt; heroes breathe and blink while they stand.
181
+
182
+ **Pixel-art settings** (`settings` in the graph; the assembler sets the first two from the game's theme):
183
+
184
+ | Setting | Values | What it does |
185
+ |---|---|---|
186
+ | `pixel_palette` | `none`, `pico8`, `sweetie16`, `endesga32` | Limits every sprite, tile and backdrop colour to a classic palette, so the whole game shares one set of colours. |
187
+ | `scenery` | `auto`, `flat`, or a theme (`night`, `forest`, `desert`, `neon`, `candy`, `ocean`, `lava`, `snow`) | A banded sky with sun or moon and stars, parallax clouds, mountains, hills and themed trees, cacti, skylines or kelp (a themed floor for top-down games). `auto` picks one that fits the background colour, `flat` keeps the plain hills. |
188
+ | `pixel_size` | `2` (default), `1` | `2` is chunky (NES-like); `1` draws the same art at twice the resolution (SNES-like) at the same on-screen size. |
189
+
190
+ <p align="center"><img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-size.png" width="92%" alt="The same game with pixel_size 2 (chunky) and pixel_size 1 (fine)"><br><sub>The same game, same moment: <code>pixel_size: 2</code> (left) and <code>pixel_size: 1</code> (right).</sub></p>
191
+
192
+ Ground tiles are drawn for their neighbours (grass on top, outlined exposed edges, rounded and notched corners,
193
+ varied interiors, capped one-way planks), and pixel-art levels get pixel effects: skidding dust, hit sparks and a
194
+ screen shake in whole pixels.
165
195
 
166
196
  **Collision comes from the pixels.** Hitboxes, hurtboxes, pickups, hazards and static bodies use the
167
197
  sprite's traced outline (`CollisionPolygon2D`); moving bodies keep a smooth capsule/circle fitted to
@@ -325,8 +355,13 @@ headless smoke test (spawn, scripted input, crash detection) and report `E_RUNTI
325
355
  |---|---|
326
356
  | Movement | `PlatformerMovement` (coyote time, jump buffer, variable jump, apex hang, dash, double jump), `TopDownMovement` (accel/friction, dash) |
327
357
  | Controllers | `PlayerInput`, `PatrolAI`, `ChaseAI`, `Aim` |
328
- | Combat | `Health`, `Hitbox`, `Hurtbox`, `Stomp`, `Weapon`, `Projectile` |
329
- | World | `Collectible`, `TriggerZone`, `Spawner`, `Gate` |
358
+ | Combat | `Health`, `Hitbox`, `Hurtbox`, `Stomp`, `Weapon`, `Projectile` (a sword is a `Weapon` whose projectile is a short, piercing slash) |
359
+ | World | `Collectible`, `TriggerZone`, `Spawner` (ramping waves, spawns around the player), `Gate` |
360
+ | Progression | `Experience` (XP and levels), `XpOrb` (gems that fly to the player), `LevelUpMenu` (pause and pick one of three upgrades) |
361
+ | Game modes | `BubbleBoard` (a complete bubble shooter: bank shots, colour pops, falling clusters, a descending ceiling) |
362
+ | Shooters | `FlightPath` (straight, wave, dive and hover patterns across the screen), `ShmupShip` (auto-fire, power-ups that add shots and spread), `HealthBar` (boss bars), and `WaveSpawner` gates (`autostart`, `endless`, random spread) |
363
+ | Tower defense | `PathFollower` (walks the road to the base), `Base` (loses a life per invader that gets in), `WaveSpawner` ("8 x Goblin every 1.2; ..."), `TowerSlot`, `TowerBuilder` (click a pad, pay in gold), `TowerBrain` (targets the invader furthest along), `GroundPatch` (roads and paths) |
364
+ | Farming | `FarmPlot` (soil that grows a crop only while watered), `Farmer` (plant / water / harvest, a basket, crop choice), `Market` (sell the basket), `CropPest` (crows that eat crops) |
330
365
  | Feel | `Juice` — attached to every entity unless `"juice": "none"` |
331
366
 
332
367
  Runtime autoloads: `GameState` (score, lives, variables, win/lose), `World` (spawning, rule
@@ -361,6 +396,9 @@ spawnpoint manifest # regenerate manifest.json after changing a componen
361
396
  godot4 --headless --path godot --fixed-fps 60 res://tests/run_tests.tscn # GDScript suite only
362
397
  ```
363
398
 
399
+ `spawnpoint demo -o site` builds the browser demo site: the showcase graphs in `examples/`, one shared Godot
400
+ engine, each game checked in headless Chrome. The Pages workflow publishes it on every push.
401
+
364
402
  Adding a component: create `godot/addons/spawn_point/components/<name>_component.gd` following the
365
403
  contract (typed `@export`s with ranges, `SP_NAME` / `SP_HOSTS` / `SP_REQUIRES` / `SP_CATEGORY`
366
404
  constants, `##` docs on everything public), add tests in `godot/tests/`, then run `spawnpoint manifest`.
@@ -5,15 +5,26 @@
5
5
  [![CI](https://github.com/leluong141996-dev/Spawn-Point/actions/workflows/ci.yml/badge.svg)](https://github.com/leluong141996-dev/Spawn-Point/actions/workflows/ci.yml)
6
6
  [![PyPI](https://img.shields.io/pypi/v/spawnpoint-studio)](https://pypi.org/project/spawnpoint-studio/)
7
7
  [![Model](https://img.shields.io/badge/%F0%9F%A4%97%20model-SpawnPoint--Coder--7B-yellow)](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-AWQ)
8
+ [![Play the demos](https://img.shields.io/badge/%E2%96%B6%20play-demos%20in%20your%20browser-ff004d)](https://leluong141996-dev.github.io/Spawn-Point/)
8
9
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
9
10
 
10
11
  <p align="center">
11
- <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-hopper.gif" width="48%" alt="Pixel Hopper — generated platformer">
12
- <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-arena.gif" width="48%" alt="Pixel Arena — generated top-down shooter">
12
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/knight-quest.gif" width="48%" alt="Knight's Quest: a cartoon-style sword platformer">
13
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/castle-defense.gif" width="48%" alt="Castle Defense: a tower defense game">
14
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/ninja-rescue.gif" width="48%" alt="Ninja Rescue: a platformer where a ninja frees his mother">
15
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/dino-eggs.gif" width="48%" alt="Dino Eggs: a bubble shooter with dinosaur eggs">
16
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/night-survivor.gif" width="48%" alt="Night Survivor: auto-fire, gems, level-ups">
17
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-hopper.gif" width="48%" alt="Pixel Hopper: a Mario-style platformer">
18
+ <img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/star-run.gif" width="48%" alt="Star Run: a side-scrolling shooter with power-ups and a boss">
13
19
  </p>
14
- <p align="center"><sub>Both games are built from <code>examples/pixel_hopper.json</code> and
15
- <code>examples/pixel_arena.json</code>: generated sprites, collision traced from pixels, juice on by
16
- default. Recorded with <code>spawnpoint gif</code> (autopilot).</sub></p>
20
+ <p align="center"><sub>Seven of the demo games, each one a small JSON graph in <code>examples/</code> built from the
21
+ same component library: generated cartoon or pixel art, collision traced from the sprites, juice on by default.
22
+ Recorded with <code>spawnpoint gif</code> (autopilot).</sub></p>
23
+
24
+ <p align="center"><b><a href="https://leluong141996-dev.github.io/Spawn-Point/">▶ Play the demos in your browser</a></b>: a Mario-style platformer, a cartoon
25
+ knight with a sword, tower defense, a ninja rescue, a dinosaur-egg bubble shooter, a survivor with level-ups, a farming
26
+ game, an arena shooter and a side-scrolling space shooter. No install needed, and
27
+ they work on phones too (touch controls).</p>
17
28
 
18
29
  Spawn Point is an open-source, autonomous game studio that runs on your own machine. Instead of asking an LLM to type
19
30
  hundreds of lines of GDScript (slow, expensive, and easy to break with Godot 3/4 mix-ups), the LLM
@@ -38,16 +49,16 @@ graph.json ──► validate (Python) ──► build scenes (Godot headless)
38
49
  ## Install
39
50
 
40
51
  Requirements: Python ≥ 3.10, [Godot 4.6](https://godotengine.org/download) on your `PATH` (`godot4` or
41
- `godot`, or set `GODOT_BIN`), and for the LLM features either a 12 GB NVIDIA GPU with Docker (local model)
42
- or any OpenAI-compatible API.
52
+ `godot`, or set `GODOT_BIN`), and for the LLM features one of: a 12 GB NVIDIA GPU with Docker (vLLM, fastest),
53
+ [Ollama](https://ollama.com/download) on any machine (Mac, AMD, or CPU), or any OpenAI-compatible API.
43
54
 
44
55
  ```bash
45
56
  pip install spawnpoint-studio
46
- spawnpoint llm up # serves SpawnPoint-Coder-7B locally (or set SPAWNPOINT_LLM_* for an API)
57
+ spawnpoint llm up # SpawnPoint-Coder-7B: vLLM on NVIDIA, otherwise Ollama (or set SPAWNPOINT_LLM_*)
47
58
  spawnpoint create "a ninja platformer where you collect stars"
48
59
  ```
49
60
 
50
- No GPU? Try the hand-written examples, no LLM needed:
61
+ No GPU? [Play the demos in your browser](https://leluong141996-dev.github.io/Spawn-Point/), or build the hand-written examples locally, no LLM needed:
51
62
 
52
63
  ```bash
53
64
  spawnpoint examples copy pixel_hopper && spawnpoint build pixel_hopper.json --run
@@ -78,7 +89,7 @@ Standard library only on the server, one HTML/JS page on the client, Server-Sent
78
89
  ## Make a game from a sentence
79
90
 
80
91
  ```bash
81
- scripts/llm-up.sh # SpawnPoint-Coder-7B on vLLM (~11 GB VRAM) — or any OpenAI-compatible API
92
+ spawnpoint llm up # SpawnPoint-Coder-7B: vLLM (~11 GB VRAM) or Ollama — or any OpenAI-compatible API
82
93
  spawnpoint create "a ninja platformer where you collect stars"
83
94
  ```
84
95
 
@@ -121,23 +132,42 @@ Only what you asked for changes, and the level layout is kept. Prompts can be in
121
132
  **The model.** [SpawnPoint-Coder-7B](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-AWQ) is a 7B model
122
133
  fine-tuned to generate Godot 4 games through this pipeline: it writes the game design, tweaks and entity
123
134
  edits as strict JSON, and the compiler turns that into Godot scenes and GDScript. It runs on a single 12 GB
124
- GPU (AWQ 4-bit). v2 on 30 human-written game ideas (English and Vietnamese): 87% fully right (v1: 73%),
125
- 97% of checks passed; on held-out tasks: briefs 88% exact (v1: 36%), Vietnamese → English 97% (v1: 52%),
126
- tweaks 98%, entity edits 91%. It is tuned for 2D platformers and 2D top-down games only; other genres
127
- are no better than a general 7B coder. `scripts/llm-up.sh` downloads and serves it.
135
+ GPU (AWQ 4-bit). v5 adds **side-scrolling shooters** (flight patterns, power-ups, a boss), reads **Vietnamese prompts directly** and writes a small per-genre format instead of one big form. The downloadable AWQ build gets 75% of a sealed set of 16 human-written prompts for shooters and the v4 fixes fully right, and 77% of 30 human-written game ideas in English and Vietnamese (v4 scored a bit higher on its older sets; the model card lists every number and where v5 is behind). The GGUF Q4_K_M build scores 73% on those 30. Older models still work with `SPAWNPOINT_BRIEF_FORMAT=v4`. It is tuned for the 2D game kinds Spawn Point builds; other genres are no better than a general 7B coder. `spawnpoint llm up` downloads and serves it: the AWQ build on vLLM when
136
+ Docker and an NVIDIA GPU are present, otherwise the [GGUF build](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-GGUF)
137
+ (Q4_K_M, ~4.7 GB) in Ollama. Force one with `--backend vllm|ollama`.
128
138
 
129
- LLM settings: `SPAWNPOINT_LLM_BASE_URL` (default `http://localhost:8000/v1`),
130
- `SPAWNPOINT_LLM_MODEL` (default: the server's first model), `SPAWNPOINT_LLM_API_KEY`.
139
+ LLM settings: `SPAWNPOINT_LLM_BASE_URL` (default: vLLM at `http://localhost:8000/v1`, or Ollama at
140
+ `http://localhost:11434/v1` when only Ollama answers), `SPAWNPOINT_LLM_MODEL` (default: SpawnPoint-Coder if the
141
+ server has it, else its first model), `SPAWNPOINT_LLM_API_KEY`.
131
142
  Web builds need the Godot **web export templates** (Editor → Manage Export Templates, or unpack
132
143
  `web_*.zip` from the official `.tpz` into `~/.local/share/godot/export_templates/<version>/`);
133
144
  use `--no-web` to skip and play natively with `spawnpoint run`.
134
145
 
135
146
  ## Sprites: generated or your own
136
147
 
137
- Layer 2 replaces placeholder shapes with **procedural pixel-art sprites** — no assets, no GPU. The
148
+ Layer 2 replaces placeholder shapes with **procedural sprites** — no assets, no GPU — in one of two art styles:
149
+ **pixel art** (the default) or **cartoon** (`"art_style": "cartoon"` in the graph settings, or "cartoon style" in
150
+ the prompt): smooth shaded sprites with a sticker outline, rounded tiles, and a parallax sky with hills, clouds and
151
+ trees. The
138
152
  kind follows the entity (`Goblin` → humanoid, `FireBat` → flyer, `LaserBot` → robot, `Crystal` → gem…)
139
- and details follow the name (`Ninja` → mask, `Fox` → ears and tail, `Spider` → legs). Every sprite has
140
- idle / run / jump / fall / hurt animations driven automatically by an `Animator` component.
153
+ and details follow the name (`Ninja` → mask, `Fox` → ears and tail, `Spider` → legs, a sword-wielder holds a sword).
154
+ Every sprite has idle / run / jump / fall / hurt animations driven automatically by an `Animator` component, plus
155
+ `attack` (played each time its weapon fires) and `die` (a death sequence that plays on after the entity is gone) for
156
+ anything that fights or can be hurt; heroes breathe and blink while they stand.
157
+
158
+ **Pixel-art settings** (`settings` in the graph; the assembler sets the first two from the game's theme):
159
+
160
+ | Setting | Values | What it does |
161
+ |---|---|---|
162
+ | `pixel_palette` | `none`, `pico8`, `sweetie16`, `endesga32` | Limits every sprite, tile and backdrop colour to a classic palette, so the whole game shares one set of colours. |
163
+ | `scenery` | `auto`, `flat`, or a theme (`night`, `forest`, `desert`, `neon`, `candy`, `ocean`, `lava`, `snow`) | A banded sky with sun or moon and stars, parallax clouds, mountains, hills and themed trees, cacti, skylines or kelp (a themed floor for top-down games). `auto` picks one that fits the background colour, `flat` keeps the plain hills. |
164
+ | `pixel_size` | `2` (default), `1` | `2` is chunky (NES-like); `1` draws the same art at twice the resolution (SNES-like) at the same on-screen size. |
165
+
166
+ <p align="center"><img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/pixel-size.png" width="92%" alt="The same game with pixel_size 2 (chunky) and pixel_size 1 (fine)"><br><sub>The same game, same moment: <code>pixel_size: 2</code> (left) and <code>pixel_size: 1</code> (right).</sub></p>
167
+
168
+ Ground tiles are drawn for their neighbours (grass on top, outlined exposed edges, rounded and notched corners,
169
+ varied interiors, capped one-way planks), and pixel-art levels get pixel effects: skidding dust, hit sparks and a
170
+ screen shake in whole pixels.
141
171
 
142
172
  **Collision comes from the pixels.** Hitboxes, hurtboxes, pickups, hazards and static bodies use the
143
173
  sprite's traced outline (`CollisionPolygon2D`); moving bodies keep a smooth capsule/circle fitted to
@@ -301,8 +331,13 @@ headless smoke test (spawn, scripted input, crash detection) and report `E_RUNTI
301
331
  |---|---|
302
332
  | Movement | `PlatformerMovement` (coyote time, jump buffer, variable jump, apex hang, dash, double jump), `TopDownMovement` (accel/friction, dash) |
303
333
  | Controllers | `PlayerInput`, `PatrolAI`, `ChaseAI`, `Aim` |
304
- | Combat | `Health`, `Hitbox`, `Hurtbox`, `Stomp`, `Weapon`, `Projectile` |
305
- | World | `Collectible`, `TriggerZone`, `Spawner`, `Gate` |
334
+ | Combat | `Health`, `Hitbox`, `Hurtbox`, `Stomp`, `Weapon`, `Projectile` (a sword is a `Weapon` whose projectile is a short, piercing slash) |
335
+ | World | `Collectible`, `TriggerZone`, `Spawner` (ramping waves, spawns around the player), `Gate` |
336
+ | Progression | `Experience` (XP and levels), `XpOrb` (gems that fly to the player), `LevelUpMenu` (pause and pick one of three upgrades) |
337
+ | Game modes | `BubbleBoard` (a complete bubble shooter: bank shots, colour pops, falling clusters, a descending ceiling) |
338
+ | Shooters | `FlightPath` (straight, wave, dive and hover patterns across the screen), `ShmupShip` (auto-fire, power-ups that add shots and spread), `HealthBar` (boss bars), and `WaveSpawner` gates (`autostart`, `endless`, random spread) |
339
+ | Tower defense | `PathFollower` (walks the road to the base), `Base` (loses a life per invader that gets in), `WaveSpawner` ("8 x Goblin every 1.2; ..."), `TowerSlot`, `TowerBuilder` (click a pad, pay in gold), `TowerBrain` (targets the invader furthest along), `GroundPatch` (roads and paths) |
340
+ | Farming | `FarmPlot` (soil that grows a crop only while watered), `Farmer` (plant / water / harvest, a basket, crop choice), `Market` (sell the basket), `CropPest` (crows that eat crops) |
306
341
  | Feel | `Juice` — attached to every entity unless `"juice": "none"` |
307
342
 
308
343
  Runtime autoloads: `GameState` (score, lives, variables, win/lose), `World` (spawning, rule
@@ -337,6 +372,9 @@ spawnpoint manifest # regenerate manifest.json after changing a componen
337
372
  godot4 --headless --path godot --fixed-fps 60 res://tests/run_tests.tscn # GDScript suite only
338
373
  ```
339
374
 
375
+ `spawnpoint demo -o site` builds the browser demo site: the showcase graphs in `examples/`, one shared Godot
376
+ engine, each game checked in headless Chrome. The Pages workflow publishes it on every push.
377
+
340
378
  Adding a component: create `godot/addons/spawn_point/components/<name>_component.gd` following the
341
379
  contract (typed `@export`s with ranges, `SP_NAME` / `SP_HOSTS` / `SP_REQUIRES` / `SP_CATEGORY`
342
380
  constants, `##` docs on everything public), add tests in `godot/tests/`, then run `spawnpoint manifest`.
@@ -46,6 +46,7 @@ class Analysis:
46
46
  rules_source: str | None = None
47
47
  sprites: dict[str, SpriteSet] = field(default_factory=dict) # entity -> resolved sprite sheet
48
48
  tiles_image: Any = None # pixel-art tile atlas (PIL image), written to res://sprites/tiles.png
49
+ scenery_images: dict[str, Any] = field(default_factory=dict) # name -> PIL image, written to res://sprites/<name>.png
49
50
 
50
51
  @property
51
52
  def ok(self) -> bool:
@@ -69,6 +70,7 @@ class _Analyzer:
69
70
  self.genre = GENRE_DEFAULTS[graph.genre]
70
71
  self.sprites: dict[str, SpriteSet] = {}
71
72
  self.tiles_image = None
73
+ self.scenery_images: dict[str, Any] = {}
72
74
 
73
75
  # -- entry -----------------------------------------------------------------------------
74
76
  def run(self) -> Analysis:
@@ -99,7 +101,7 @@ class _Analyzer:
99
101
  if not self.d.ok or data is None:
100
102
  return Analysis(g, self.d)
101
103
  plan = self._plan(data, teams, params_by_entity, players[0])
102
- return Analysis(g, self.d, plan, rules.source, self.sprites, self.tiles_image)
104
+ return Analysis(g, self.d, plan, rules.source, self.sprites, self.tiles_image, self.scenery_images)
103
105
 
104
106
  # -- sprites ---------------------------------------------------------------------------
105
107
  def _resolve_sprites(self, teams: dict[str, str]) -> None:
@@ -110,7 +112,10 @@ class _Analyzer:
110
112
  try:
111
113
  path = join_path("entities", name, "sprite")
112
114
  sprite = sprite_resolve.resolve(name, ent, self.base_dir, color,
113
- warn=lambda m, p=path: self.d.warning("W_AI_SPRITE", p, m))
115
+ warn=lambda m, p=path: self.d.warning("W_AI_SPRITE", p, m),
116
+ style=self.g.settings.art_style,
117
+ palette=self.g.settings.pixel_palette,
118
+ fine=self.g.settings.pixel_size == 1)
114
119
  except SpriteError as e:
115
120
  self.d.error("E_SPRITE", join_path("entities", name, "sprite"), str(e),
116
121
  "Generator kinds: " + ", ".join(sorted(sprite_resolve.KINDS)) if "kind" in str(e) else None)
@@ -120,6 +125,37 @@ class _Analyzer:
120
125
  # No explicit shape: the body collision comes from the drawing.
121
126
  ent.shape = sprite_resolve.derived_shape(ent, sprite, self.g.genre)
122
127
 
128
+ def _scenery(self, background: str) -> dict | None:
129
+ """Pixel-art backdrop images (written next to the sprites) and how the level should lay them out."""
130
+ s = self.g.settings
131
+ from .sprites import scenery as sc
132
+
133
+ if s.scenery == "space": # scrolling stars for shooters, in any art style
134
+ sky, stars = sc.space(background, s.pixel_palette if s.art_style == "pixel" else None, s.seed)
135
+ self.scenery_images["scenery_sky"] = sc.to_image(sky)
136
+ layers = []
137
+ for name, image, speed in stars:
138
+ self.scenery_images[f"scenery_stars_{name}"] = sc.to_image(image)
139
+ layers.append({"name": name, "texture": f"res://sprites/scenery_stars_{name}.png", "speed": speed,
140
+ "height": int(image.shape[0])})
141
+ return {"sky": "res://sprites/scenery_sky.png", "stars": layers, "pixel": 2, "width": sc.WIDTH}
142
+ if s.art_style != "pixel" or s.scenery == "flat" or s.tile_style != "pixel":
143
+ return None
144
+
145
+ theme = s.scenery if s.scenery in sc.THEMES else "auto"
146
+ if self.g.genre == "topdown":
147
+ self.scenery_images["scenery_floor"] = sc.to_image(sc.floor_tile(theme, background, s.pixel_palette, s.seed))
148
+ return {"floor": "res://sprites/scenery_floor.png", "pixel": 2}
149
+ sky, layers = sc.build(theme, background, s.pixel_palette, s.seed)
150
+ self.scenery_images["scenery_sky"] = sc.to_image(sky)
151
+ out = []
152
+ for layer in layers:
153
+ name = f"scenery_{layer.name}"
154
+ self.scenery_images[name] = sc.to_image(layer.image)
155
+ out.append({"name": layer.name, "texture": f"res://sprites/{name}.png", "scroll": layer.scroll,
156
+ "anchor": layer.anchor, "lift": layer.lift, "height": int(layer.image.shape[0])})
157
+ return {"sky": "res://sprites/scenery_sky.png", "layers": out, "pixel": 2, "width": sc.WIDTH}
158
+
123
159
  # -- entities --------------------------------------------------------------------------
124
160
  def _team(self, e: Entity) -> str:
125
161
  if e.team:
@@ -331,11 +367,26 @@ class _Analyzer:
331
367
  "top": (solid and solid.top) or self.genre["solid_top"]},
332
368
  "oneway": {"color": (oneway and oneway.color) or self.genre["oneway"]},
333
369
  }
370
+ variant_of: dict[tuple[int, int], int] = {}
334
371
  if s.tile_style == "pixel":
335
- from .sprites.tiles import tileset
336
- self.tiles_image = tileset(g.genre, tiles_style["solid"]["color"], tiles_style["solid"]["top"],
337
- tiles_style["oneway"]["color"], ts, s.seed)
372
+ from .sprites.tiles import tileset_cartoon
373
+ if s.art_style == "pixel":
374
+ from .sprites.tiles import tileset_auto
375
+ # Pixel ground is drawn for each cell's real neighbours (grass on top, outlines, rounded corners).
376
+ self.tiles_image, kinds, variant_of = tileset_auto(
377
+ g.genre, tiles_style["solid"]["color"], tiles_style["solid"]["top"], tiles_style["oneway"]["color"],
378
+ ts, s.seed, s.pixel_palette, data.tiles, data.width, data.height, s.pixel_size)
379
+ tiles_style["variants"] = kinds
380
+ else:
381
+ self.tiles_image = tileset_cartoon(g.genre, tiles_style["solid"]["color"], tiles_style["solid"]["top"],
382
+ tiles_style["oneway"]["color"], ts, s.seed)
338
383
  tiles_style["texture"] = "res://sprites/tiles.png"
384
+ tiles_style["smooth"] = s.art_style == "cartoon"
385
+ if s.art_style == "cartoon":
386
+ from .sprites.tiles import CARTOON_GAP
387
+ tiles_style["separation"] = CARTOON_GAP
388
+ background = s.background or self.genre["background"]
389
+ scenery_plan = self._scenery(background)
339
390
  return {
340
391
  "format": PLAN_FORMAT,
341
392
  "name": g.name,
@@ -344,11 +395,16 @@ class _Analyzer:
344
395
  "lives": s.lives,
345
396
  "viewport": list(s.viewport),
346
397
  "zoom": s.zoom,
347
- "background": s.background or self.genre["background"],
398
+ "art_style": s.art_style,
399
+ "background": background,
400
+ **({"scenery": scenery_plan} if scenery_plan else {}),
348
401
  "gravity": s.gravity if s.gravity is not None else self.genre["gravity"],
349
402
  "tiles": tiles_style,
350
403
  "map": {"width": data.width, "height": data.height,
351
- "cells": [[x, y, k] for (x, y), k in sorted(data.tiles.items(), key=lambda kv: (kv[0][1], kv[0][0]))]},
404
+ "cells": [[x, y, k] for (x, y), k in sorted(data.tiles.items(), key=lambda kv: (kv[0][1], kv[0][0]))],
405
+ # parallel to cells: the atlas variant drawn for each one (neighbour-aware pixel tiles)
406
+ **({"cell_variants": [variant_of[c] for c, _ in sorted(data.tiles.items(), key=lambda kv: (kv[0][1], kv[0][0]))]}
407
+ if variant_of else {})},
352
408
  "bounds": [0, 0, data.width * ts, data.height * ts],
353
409
  "walls": self.genre["walls"],
354
410
  "entities": entities,
@@ -65,7 +65,8 @@ def build(data: Any, out_dir: Path, *, manifest: Manifest | None = None, smoke:
65
65
 
66
66
  t = time.perf_counter()
67
67
  feature = _godot_feature()
68
- project.write(out_dir, data, plan, analysis.rules_source, feature, analysis.sprites, analysis.tiles_image)
68
+ project.write(out_dir, data, plan, analysis.rules_source, feature, analysis.sprites, analysis.tiles_image,
69
+ analysis.scenery_images)
69
70
  sfx.write_all(out_dir / "sfx", seed=analysis.graph.settings.seed)
70
71
  timings["write"] = time.perf_counter() - t
71
72
 
@@ -142,5 +143,7 @@ def _judge_smoke(report: dict, plan: dict, diags: Diagnostics) -> None:
142
143
  return
143
144
  if plan["genre"] == "platformer" and not report.get("on_floor_after_1s"):
144
145
  diags.warning("W_SMOKE_FLOOR", "(smoke)", "the player is not standing on a floor after 1 second")
145
- if report.get("moved_px", 0) < 4:
146
+ player = next((e for e in plan["entities"] if any(c["name"] == "PlayerInput" for c in e["components"])), None)
147
+ moves = player is None or any(c["name"] in ("PlatformerMovement", "TopDownMovement") for c in player["components"])
148
+ if moves and report.get("moved_px", 0) < 4: # a bubble-shooter launcher does not walk
146
149
  diags.warning("W_SMOKE_STUCK", "(smoke)", "the player did not move under scripted input (stuck?)")
@@ -73,20 +73,27 @@ def main(argv: list[str] | None = None) -> int:
73
73
  tw.add_argument("--no-web", action="store_true")
74
74
  tw.add_argument("--record", action="store_true", help="save LLM calls as local fine-tuning data")
75
75
 
76
- lm = sub.add_parser("llm", help="run SpawnPoint-Coder-7B locally (Docker + NVIDIA GPU)")
76
+ lm = sub.add_parser("llm", help="run SpawnPoint-Coder-7B locally (vLLM on NVIDIA, or Ollama anywhere)")
77
77
  lm.add_argument("action", choices=["up", "down", "status"])
78
78
  lm.add_argument("--port", type=int, default=8000)
79
+ lm.add_argument("--backend", choices=["auto", "vllm", "ollama"], default="auto",
80
+ help="auto: vLLM with Docker + an NVIDIA GPU, otherwise Ollama")
79
81
 
80
82
  ex = sub.add_parser("examples", help="list the example graphs, or copy one here")
81
83
  ex.add_argument("action", nargs="?", choices=["list", "copy"], default="list")
82
84
  ex.add_argument("name", nargs="?")
83
85
 
86
+ dm = sub.add_parser("demo", help="build every example as a web game plus a gallery page (static site)")
87
+ dm.add_argument("-o", "--out", type=Path, default=Path("site"))
88
+ dm.add_argument("--no-screenshots", action="store_true", help="skip the headless-Chrome check and thumbnails")
89
+
84
90
  gf = sub.add_parser("gif", help="record a gameplay GIF of a built project (autopilot; needs a display)")
85
91
  gf.add_argument("project", type=Path)
86
92
  gf.add_argument("-o", "--out", type=Path, help="output file (default: <project>/demo.gif)")
87
93
  gf.add_argument("--seconds", type=float, default=8.0)
88
94
  gf.add_argument("--fps", type=int, default=20)
89
95
  gf.add_argument("--width", type=int, default=640)
96
+ gf.add_argument("--skip", type=float, default=0.0, help="seconds of play before the clip starts")
90
97
 
91
98
  sp = sub.add_parser("sprites", help="import, generate or preview sprites")
92
99
  sps = sp.add_subparsers(dest="sprites_cmd", required=True)
@@ -130,7 +137,8 @@ def main(argv: list[str] | None = None) -> int:
130
137
  return {"validate": _validate, "build": _build, "run": _run, "components": _components,
131
138
  "manifest": _manifest, "create": studio_cli.create, "tweak": studio_cli.tweak,
132
139
  "serve": studio_cli.serve, "dataset": studio_cli.dataset, "playtest": studio_cli.playtest, "studio": studio_cli.studio,
133
- "sprites": _sprites, "gif": _gif, "llm": _llm, "examples": _examples}[args.cmd](args)
140
+ "sprites": _sprites, "gif": _gif, "llm": _llm, "examples": _examples,
141
+ "demo": _demo}[args.cmd](args)
134
142
  except godot.GodotError as e:
135
143
  print(f"error: {e}", file=sys.stderr)
136
144
  if e.output:
@@ -207,8 +215,9 @@ def _llm(args) -> int:
207
215
  from . import llm_server
208
216
 
209
217
  try:
210
- return {"up": lambda: llm_server.up(args.port), "down": llm_server.down,
211
- "status": lambda: llm_server.status(args.port)}[args.action]()
218
+ return {"up": lambda: llm_server.up(args.port, backend=args.backend),
219
+ "down": lambda: llm_server.down(backend=args.backend),
220
+ "status": lambda: llm_server.status(args.port, backend=args.backend)}[args.action]()
212
221
  except (RuntimeError, subprocess.CalledProcessError) as e:
213
222
  print(f"error: {e}", file=sys.stderr)
214
223
  return 2
@@ -237,11 +246,23 @@ def _gif(args) -> int:
237
246
  from .gif import record_gif
238
247
 
239
248
  out = args.out or args.project / "demo.gif"
240
- path = record_gif(args.project.resolve(), out.resolve(), args.seconds, args.fps, args.width)
249
+ path = record_gif(args.project.resolve(), out.resolve(), args.seconds, args.fps, args.width, args.skip)
241
250
  print(f"Wrote {out} ({path.stat().st_size / 1e6:.1f} MB)")
242
251
  return 0
243
252
 
244
253
 
254
+ def _demo(args) -> int:
255
+ from .demo import build_site
256
+
257
+ print(f"Building the demo site in {args.out}/")
258
+ games = build_site(args.out, screenshots=not args.no_screenshots)
259
+ failed = [g for g in games if g.errors]
260
+ for g in failed:
261
+ print(f"error: {g.slug}: " + "; ".join(g.errors[:3]), file=sys.stderr)
262
+ print(f"{len(games) - len(failed)}/{len(games)} games ok. Preview: python -m http.server -d {args.out}")
263
+ return 1 if failed else 0
264
+
265
+
245
266
  def _sprites(args) -> int:
246
267
  from .sprites import generate as gen, importer
247
268
  from .sprites.spriteset import SpriteError, load