Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / 56e777c

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

C++ 72.3% C 26.2%
git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gz

Improve levels With Claude

Christian Immanuel · 2026-06-03 07:15 · 56e777c4e312df5ec03b1797867247051fa937ce

 src/levels/level_list.cpp | 232 +++++++++++++++++++++++++++++++++-------------
 src/states/game_state.cpp |   2 +-
 2 files changed, 168 insertions(+), 66 deletions(-)

diff --git a/src/levels/level_list.cpp b/src/levels/level_list.cpp
index d9ee3a0..b68a362 100644
--- a/src/levels/level_list.cpp
+++ b/src/levels/level_list.cpp
@@ -1,27 +1,42 @@
 #include "level_list.hpp"
 #include "utils/config.hpp"
 
+// ─────────────────────────────────────────────────────────────────────────────
+// Layout conventions for the tutorial
+//
+//  * The player spawns near the world origin (0,0,0). Keep the FIRST thing a
+//    step asks for at least ~4 units away so nothing spawns under the player.
+//
+//  * "Continue plates" (ReachPlate goals that advance the tutorial) sit ON TOP
+//    of a solid box once jumping has been taught (step 1+). Elevating them means
+//    you can't drift onto them by accident — you have to deliberately jump up.
+//    The pattern is always: a box, and a plate centred on its top face.
+//
+//  * floor_y == -WORLD_BOX_SCALE. Heights are written as -WORLD_BOX_SCALE + dy.
+// ─────────────────────────────────────────────────────────────────────────────
+
 static const DynArray<Level> levels =
 {
     {
         .name = "Tutorial",
-        .desc = "Learn the controls.",
+        .desc = "Learn the controls and the code-building loop.",
         .steps = {
 
-            // ── Step 0: walk to plate ────────────────────────────────────────
+            // ── Step 0: walk to plate (floor — before jumping) ───────────────
             {
                 .hint    = "Walk to the green plate",
                 .details = R"(Welcome.
 
 Use W, A, S, D to walk.
+Move the mouse to turn.
 
-A green plate is on the floor nearby.
+A green plate sits on the floor a short walk away.
 Step onto it to continue.)",
                 .grants  = { },
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 4.0, static_cast<double>(-WORLD_BOX_SCALE), 4.0 }, .tag = "" },
+                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE), 6.0 }, .tag = "" },
                     },
                     .platforms = {},
                     .ai_seeds  = {},
@@ -31,22 +46,27 @@ Step onto it to continue.)",
                 .max_ai_words = 0,
             },
 
-            // ── Step 1: jump on a high plate ─────────────────────────────────
+            // ── Step 1: learn to jump (staircase to an elevated plate) ───────
             {
-                .hint    = "Jump onto the elevated plate",
-                .details = R"(You can jump with SPACE.
+                .hint    = "Jump up the steps to the plate",
+                .details = R"(Press SPACE to jump.
 
-A plate is sitting on top of a platform.
+Ahead is a short staircase of blocks.
+Hop up them, one at a time, and stand on the
+plate at the top.
 
-Climb up and stand on it.)",
+From now on, plates that advance the tutorial sit
+up high — you'll need this jump to reach them.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {},
-                    .plates = { { .pos = {-1.0, static_cast<double>(-WORLD_BOX_SCALE + 2.4f ),-1.0 }, .tag = "" } },
+                    .plates = {
+                        { .pos = { 8.6, static_cast<double>(-WORLD_BOX_SCALE + 2.2f), 0.0 }, .tag = "" },
+                    },
                     .platforms = {
-                        { .pos = { 2.0, static_cast<double>(-WORLD_BOX_SCALE + 0.4f ), 2.0 }, .half_extents = { 0.8, 0.2, 0.8 } },
-                        { .pos = { 0.5, static_cast<double>(-WORLD_BOX_SCALE + 1.2f ), 0.5 }, .half_extents = { 0.8, 0.2, 0.8 } },
-                        { .pos = {-1.0, static_cast<double>(-WORLD_BOX_SCALE + 2.0f ),-1.0 }, .half_extents = { 0.8, 0.2, 0.8 } },
+                        { .pos = { 5.0, static_cast<double>(-WORLD_BOX_SCALE + 0.4f), 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
+                        { .pos = { 6.8, static_cast<double>(-WORLD_BOX_SCALE + 1.1f), 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
+                        { .pos = { 8.6, static_cast<double>(-WORLD_BOX_SCALE + 1.8f), 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
                     },
                     .ai_seeds  = {},
                 },
@@ -55,23 +75,29 @@ Climb up and stand on it.)",
                 .max_ai_words = 0,
             },
 
-            // ── Step 2: look around ──────────────────────────────────────────
+            // ── Step 2: look around (elevated continue-plate) ────────────────
             {
-                .hint    = "Look at each wall, then step on the plate",
+                .hint    = "Look at each wall, then jump onto the plate",
                 .details = R"(Move the mouse to look around.
 
 Four walls surround you:
-  - Editor:   where you build code
+  - Editor:   where you build code (the pults face it)
   - Compiler: shows compile messages
-  - Program:  shows program output
+  - Program:  shows your program's output
   - Info:     this wall, with hints
 
-When you've seen them all, step onto the plate.)",
+Tip: hold C to free-look without turning your body.
+
+When you've seen them all, jump onto the plate.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words  = {},
-                    .plates = { { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE), 3.0 }, .tag = "" } },
-                    .platforms = {},
+                    .plates = {
+                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 1.2f), 6.0 }, .tag = "" },
+                    },
+                    .platforms = {
+                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 0.6f), 6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
+                    },
                     .ai_seeds  = {},
                 },
                 .reset_carry  = true,
@@ -81,15 +107,15 @@ When you've seen them all, step onto the plate.)",
 
             // ── Step 3: pick up a word ───────────────────────────────────────
             {
-                .hint    = "Walk into the floating word and pick it up.",
+                .hint    = "Walk into the floating word and press E",
                 .details = R"(Words float in the air.
 
-Walk into "hello" to pick it up by pressing 'e'
-Picked words trail in front of you.)",
+Walk up to "hello" and press E to pick it up.
+Picked words trail in front of you as you move.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .text = "hello", .pos = { 4.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), 0.0 } },
+                        { .text = "hello", .pos = { 5.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), 0.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
@@ -100,23 +126,19 @@ Picked words trail in front of you.)",
                 .max_ai_words = 0,
             },
 
-            // ── Step 4: pick a sequence ──────────────────────────────────────
-            //
-            // Real improvement over the old version: the previous code
-            // advanced on the FIRST pick. Now all three words are required.
+            // ── Step 4: pick up several words (order matters) ─────────────────
             {
-                .hint    = "Pick up the words",
+                .hint    = "Pick up all three words",
                 .details = R"(You can carry several words at once.
 
-Walk through the three "press / any / key" words.
-
-They line up in the order you picked them.)",
+Collect "press", "any" and "key" with E.
+They queue up in the order you grab them.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .text = "press", .pos = {  4.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), -2.0 } },
-                        { .text = "any",   .pos = {  0.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), -2.0 } },
-                        { .text = "key",   .pos = { -4.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), -2.0 } },
+                        { .text = "press", .pos = {  5.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), -3.0 } },
+                        { .text = "any",   .pos = {  0.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), -3.0 } },
+                        { .text = "key",   .pos = { -5.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), -3.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
@@ -134,9 +156,10 @@ They line up in the order you picked them.)",
             // ── Step 5: drop a word ──────────────────────────────────────────
             {
                 .hint    = "Press Q to drop the last word",
-                .details = R"(Q drops the last word you picked.
+                .details = R"(Q drops the most recently picked word.
 
-The dropped word falls to the floor.)",
+The dropped word falls to the floor — handy for
+putting back something you grabbed by mistake.)",
                 .grants  = {},
                 .scene   = {},
                 .reset_carry  = false,
@@ -144,12 +167,14 @@ The dropped word falls to the floor.)",
                 .max_ai_words = 0,
             },
 
-            // ── Step 6: shoot a word ─────────────────────────────────────────
+            // ── Step 6: throw a word ─────────────────────────────────────────
             {
-                .hint    = "LEFT CLICK to throw the next word",
-                .details = R"(Left click throws the most recent carried word.
+                .hint    = "LEFT CLICK to throw a word",
+                .details = R"(Left click throws the word at the front of your line.
 
-Throw a word — any direction is fine.)",
+Aim with the crosshair and throw — any direction is fine.
+
+Carrying several? Tap F to throw the WHOLE line at once.)",
                 .grants  = {},
                 .scene   = {},
                 .reset_carry  = false,
@@ -157,17 +182,15 @@ Throw a word — any direction is fine.)",
                 .max_ai_words = 0,
             },
 
-            // ── Step 7: shoot a hostile word ─────────────────────────────────
+            // ── Step 7: combat — shoot a hostile word ────────────────────────
             {
-                .hint    = "Shoot the red word",
-                .details = R"(Some words turn red and chase you.
-
-They want to steal what you carry.
+                .hint    = "Grab the ammo, then shoot the red word",
+                .details = R"(Some words turn red and chase you to steal what
+you carry.
 
-Pick up "ammo", then shoot the red word
-before it lands a hit.
-
-Aim with the crosshair and LEFT CLICK.)",
+Pick up "ammo", then knock out the red "danger"
+word by throwing at it with LEFT CLICK before it
+reaches you.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
@@ -189,20 +212,25 @@ Aim with the crosshair and LEFT CLICK.)",
                 .max_ai_words = 0,
             },
 
-            // ── Step 8: compile your first program ───────────────────────────
+            // ── Step 8: build on the editor wall + compile ───────────────────
             {
-                .hint    = "Build int main(void) { return 0; }",
-                .details = R"(Time to make code.
+                .hint    = "Build int main(void) { return 0; } and press BUILD",
+                .details = R"(Now make code.
 
-The Editor wall is behind the pults — face it,
-and throw tokens at it to build source code.
+Face the Editor wall (the pults point at it). Throw
+tokens at the wall to place them:
+  - LEFT CLICK throws one word
+  - F throws your whole carried line at once
 
-The "Compile" pult runs your program.
+Words snap into rows. Aim between two words to drop
+a token between them. Mis-placed a token? Walk up and
+press E to pluck it back off the wall.
 
-Build:
+Build exactly:
   int main(void) { return 0; }
 
-Walk to the Compile pult and press it.)",
+Then walk to the Compile pult (labelled BUILD) and
+press it.)",
                 .grants = {
                     TokenGrant::give("int"),
                     TokenGrant::give("main"),
@@ -218,18 +246,92 @@ Walk to the Compile pult and press it.)",
                 .scene        = {},
                 .reset_carry  = true,
                 .goal         = CompilePattern{ "int main ( void ) { return 0 ; }" },
-                .max_ai_words = 3,
+                .max_ai_words = 0,
+            },
+
+            // ── Step 9: make it print, then RUN it ───────────────────────────
+            {
+                .hint    = "Add printf, BUILD, then press RUN",
+                .details = R"(A program that does nothing is dull. Make it print.
+
+Add a printf line inside main, before return:
+  printf("hello");
+
+So the whole thing reads:
+  int main(void) { printf("hello"); return 0; }
+
+Press BUILD to compile, then walk to the RUN pult and
+press it. Your program runs and its output appears on
+the Program wall — and that's what finishes this step.)",
+                .grants = {
+                    TokenGrant::give("int"),
+                    TokenGrant::give("main"),
+                    TokenGrant::give("("),
+                    TokenGrant::give("void"),
+                    TokenGrant::give(")"),
+                    TokenGrant::give("{"),
+                    TokenGrant::give("printf"),
+                    TokenGrant::give("("),
+                    TokenGrant::give("\"hello\""),
+                    TokenGrant::give(")"),
+                    TokenGrant::give(";"),
+                    TokenGrant::give("return"),
+                    TokenGrant::give("0"),
+                    TokenGrant::give(";"),
+                    TokenGrant::give("}"),
+                    TokenGrant::give("#include"),
+                    TokenGrant::give("<stdio.h>"),
+                },
+                .scene        = {},
+                .reset_carry  = true,
+                // Must build the printf program (compiles + matches) AND press RUN.
+                .goal = AllOf{{
+                    CompilePattern{ "int main ( void ) { printf ( \"hello\" ) ; return 0 ; }" },
+                    PressButton{ PultAction::RunProgram },
+                }},
+                .max_ai_words = 0,
+            },
+
+            // ── Step 10: editor tools (scroll / clear / line numbers) ────────
+            {
+                .hint    = "Try the editor pults, then jump onto the plate",
+                .details = R"(A few tools for managing the Editor wall:
+
+  - The UP / DOWN pults scroll the code.
+  - Each line is numbered on both sides. Shoot a line
+    number to scroll that line to your eye level.
+  - The CLEAR pult wipes the wall.
+  - Press E on a placed word to pull it back off.
+
+Have a play, then jump onto the plate to continue.)",
+                .grants  = {
+                    TokenGrant::give("int"),
+                    TokenGrant::give("x"),
+                    TokenGrant::give("="),
+                    TokenGrant::give("0"),
+                    TokenGrant::give(";"),
+                },
+                .scene   = Scene{
+                    .words  = {},
+                    .plates = {
+                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 1.2f), -6.0 }, .tag = "" },
+                    },
+                    .platforms = {
+                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 0.6f), -6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
+                    },
+                    .ai_seeds  = {},
+                },
+                .reset_carry  = true,
+                .goal         = ReachPlate{ "" },
+                .max_ai_words = 0,
             },
 
-            // ── Step 9: finish plate ─────────────────────────────────────────
-            //
-            // Bug fix: previously advanced on JUMP, regardless of plate.
-            // Now actually requires standing on the plate.
+            // ── Step 11: finish (elevated plate → first real level) ──────────
             {
-                .hint    = "Jump onto the plate to finish",
+                .hint    = "Jump onto the plate to begin \"Char Copy\"",
                 .details = R"(You're ready.
 
-Step on the plate to start the real level:
+Jump onto the plate to start the first real level:
 "Char Copy".)",
                 .grants  = {},
                 .scene   = Scene{
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index d9868c7..18ae582 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -83,7 +83,7 @@ GameState::GameState(size_t starting_level)
     ));
     _ctx.scenery.back()->body.mode = PhysicsMode::Dynamic;
     */
-    debug_SpawnWords(0);
+    //debug_SpawnWords(0);
 }