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.
- {spawnpoint_studio-0.1.1/compiler/spawnpoint_studio.egg-info → spawnpoint_studio-0.3.0}/PKG-INFO +60 -22
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/README.md +59 -21
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/analyze.py +63 -7
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/build.py +5 -2
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cli.py +26 -5
- spawnpoint_studio-0.3.0/compiler/spawnpoint/demo.py +274 -0
- spawnpoint_studio-0.3.0/compiler/spawnpoint/demo_touch.js +118 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/gif.py +10 -7
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/graph.py +8 -0
- spawnpoint_studio-0.3.0/compiler/spawnpoint/llm_server.py +123 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/report.py +4 -1
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/runner.py +16 -1
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/tuner.py +43 -14
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/project.py +3 -1
- spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/cartoon.py +477 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/generate.py +206 -13
- spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/motion.py +72 -0
- spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/palettes.py +111 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/resolve.py +17 -4
- spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/scenery.py +471 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/spriteset.py +7 -5
- spawnpoint_studio-0.3.0/compiler/spawnpoint/sprites/tiles.py +362 -0
- spawnpoint_studio-0.3.0/compiler/spawnpoint/studio/assemble.py +1127 -0
- spawnpoint_studio-0.3.0/compiler/spawnpoint/studio/brief.py +309 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/edits.py +28 -0
- spawnpoint_studio-0.3.0/compiler/spawnpoint/studio/genres.py +248 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/levelgen.py +99 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/llm.py +32 -7
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/orchestrator.py +31 -15
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/prompts.py +50 -4
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/web.py +2 -2
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/webcheck.py +7 -3
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio_cli.py +10 -1
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0/compiler/spawnpoint_studio.egg-info}/PKG-INFO +60 -22
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/SOURCES.txt +34 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/conftest.py +7 -0
- spawnpoint_studio-0.3.0/compiler/tests/test_demo.py +80 -0
- spawnpoint_studio-0.3.0/compiler/tests/test_genres.py +207 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_live_llm.py +5 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_playtest.py +24 -7
- spawnpoint_studio-0.3.0/compiler/tests/test_sprites.py +537 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_studio.py +157 -0
- spawnpoint_studio-0.3.0/examples/castle_defense.json +341 -0
- spawnpoint_studio-0.3.0/examples/dino_eggs.json +99 -0
- spawnpoint_studio-0.3.0/examples/knight_quest.json +348 -0
- spawnpoint_studio-0.3.0/examples/night_survivor.json +448 -0
- spawnpoint_studio-0.3.0/examples/ninja_rescue.json +351 -0
- spawnpoint_studio-0.3.0/examples/star_run.json +444 -0
- spawnpoint_studio-0.3.0/examples/sunny_farm.json +223 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/animator_component.gd +18 -2
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/base_component.gd +31 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/bubble_board_component.gd +427 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/crop_pest_component.gd +77 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/experience_component.gd +53 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/farm_plot_component.gd +159 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/farmer_component.gd +158 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/flight_path_component.gd +101 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/ground_patch_component.gd +37 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/health_bar_component.gd +41 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/juice_component.gd +36 -2
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/level_up_menu_component.gd +225 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/market_component.gd +52 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/path_follower_component.gd +95 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/shmup_ship_component.gd +65 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/spawner_component.gd +23 -1
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/tower_brain_component.gd +39 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/tower_builder_component.gd +105 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/tower_slot_component.gd +47 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/wave_spawner_component.gd +106 -0
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/components/xp_orb_component.gd +67 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/manifest.json +1226 -62
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/camera_rig.gd +3 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/entity_core.gd +2 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/fx.gd +95 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/hud.gd +52 -2
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/level.gd +3 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/world.gd +43 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/build.gd +256 -11
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/playtest.gd +57 -12
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/playtest_nav.gd +3 -1
- spawnpoint_studio-0.3.0/godot/addons/spawn_point/tools/record.gd +233 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/pyproject.toml +2 -2
- spawnpoint_studio-0.1.1/compiler/spawnpoint/llm_server.py +0 -77
- spawnpoint_studio-0.1.1/compiler/spawnpoint/sprites/tiles.py +0 -102
- spawnpoint_studio-0.1.1/compiler/spawnpoint/studio/assemble.py +0 -528
- spawnpoint_studio-0.1.1/compiler/spawnpoint/studio/brief.py +0 -197
- spawnpoint_studio-0.1.1/compiler/tests/test_sprites.py +0 -261
- spawnpoint_studio-0.1.1/godot/addons/spawn_point/tools/record.gd +0 -89
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/LICENSE +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/MANIFEST.in +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/__init__.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/__init__.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/server.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/static/app.css +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/static/app.js +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/cockpit/static/index.html +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/diagnostics.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/expr.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/godot.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/level.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/manifest.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/playtest/__init__.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/py.typed +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/rules.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sfx.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/__init__.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/ai.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/contour.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/sprites/importer.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/__init__.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/creative.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint/studio/dataset.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/dependency_links.txt +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/entry_points.txt +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/requires.txt +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/spawnpoint_studio.egg-info/top_level.txt +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_ai_sprites.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_analyze.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_cockpit.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_e2e.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_expr.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_gif.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_godot_library.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_graph.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_level.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_manifest.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_rules.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_sfx.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/compiler/tests/test_web.py +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/coin_hopper.json +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/neon_arena.json +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/pixel_arena.json +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/examples/pixel_hopper.json +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/aim_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/chase_ai_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/collectible_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/gate_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/health_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/hitbox_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/hurtbox_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/patrol_ai_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/platformer_movement_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/player_input_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/projectile_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/stomp_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/top_down_movement_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/trigger_zone_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/components/weapon_component.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/cockpit.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/game_state.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/sfx.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/runtime/sp.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/shaders/flash.gdshader +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/shaders/floor_grid.gdshader +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/build.tscn +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/capture.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/capture.tscn +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/dump_manifest.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/dump_manifest.tscn +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/playtest.tscn +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/record.tscn +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/smoke.gd +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/godot/addons/spawn_point/tools/smoke.tscn +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/setup.cfg +0 -0
- {spawnpoint_studio-0.1.1 → spawnpoint_studio-0.3.0}/setup.py +0 -0
{spawnpoint_studio-0.1.1/compiler/spawnpoint_studio.egg-info → spawnpoint_studio-0.3.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spawnpoint-studio
|
|
3
|
-
Version: 0.
|
|
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
|
[](https://github.com/leluong141996-dev/Spawn-Point/actions/workflows/ci.yml)
|
|
30
30
|
[](https://pypi.org/project/spawnpoint-studio/)
|
|
31
31
|
[](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-AWQ)
|
|
32
|
+
[](https://leluong141996-dev.github.io/Spawn-Point/)
|
|
32
33
|
[](LICENSE)
|
|
33
34
|
|
|
34
35
|
<p align="center">
|
|
35
|
-
<img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/
|
|
36
|
-
<img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/
|
|
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>
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
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 #
|
|
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?
|
|
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
|
-
|
|
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).
|
|
149
|
-
|
|
150
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
[](https://github.com/leluong141996-dev/Spawn-Point/actions/workflows/ci.yml)
|
|
6
6
|
[](https://pypi.org/project/spawnpoint-studio/)
|
|
7
7
|
[](https://huggingface.co/smoky1496/SpawnPoint-Coder-7B-AWQ)
|
|
8
|
+
[](https://leluong141996-dev.github.io/Spawn-Point/)
|
|
8
9
|
[](LICENSE)
|
|
9
10
|
|
|
10
11
|
<p align="center">
|
|
11
|
-
<img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/
|
|
12
|
-
<img src="https://raw.githubusercontent.com/leluong141996-dev/Spawn-Point/main/docs/media/
|
|
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>
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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 #
|
|
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?
|
|
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
|
-
|
|
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).
|
|
125
|
-
|
|
126
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
"
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
211
|
-
"
|
|
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
|