Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / aa4c0cf

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

Cleanup With Claude

Christian Immanuel · 2026-05-31 08:16 · aa4c0cf15fdb0ed990b4bac922b8161a23d0d9ef

 src/states/game_state.cpp         | 120 ++++++++++++++++----------------------
 src/states/game_state.hpp         |   7 ++-
 src/systems/input_system.cpp      |   1 +
 src/systems/input_system.hpp      |   3 +-
 src/systems/projectile_system.cpp |  39 ++++++++-----
 5 files changed, 82 insertions(+), 88 deletions(-)

diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 971e639..fd22a20 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -174,9 +174,9 @@ GameState::on_Pick()
 
     // ── Wall text: pluck off the wall, animate into carry ───────────
     if (r.kind == PickKind::Wall) {
-        if (ctx.wall_texts[r.wall_index] && ctx.wall_texts[r.wall_index]->text3d) {
+        if (ctx.wall_texts[r.wall_index] && ctx.wall_texts[r.wall_index]->text3d)
             editor_wall.on_WordRemoved(ctx.wall_texts[r.wall_index]->text3d.get());
-        }
+
         Text3dPtr base = std::move(ctx.wall_texts[r.wall_index]->text3d);
         ctx.wall_texts.erase(ctx.wall_texts.begin() + static_cast<std::ptrdiff_t>(r.wall_index));
         if (!base) return false;
@@ -186,43 +186,21 @@ GameState::on_Pick()
             base->set_Look(Text3d::color_floor, Text3d::depth_floor);
             TextWall::show_TokenInfo(ctx.wall_texts, base->text, ctx.world->scale());
         }
-
-        WordAnim_Add anim;
-        anim.word        = std::move(base);
-        anim.start_pos   = anim.word->pos;
-        anim.yaw_start   = anim.word->yaw;
-        anim.pitch_start = anim.word->pitch;
-        anim.t           = 0.0;
-        anim.complete    = false;
-        auto [anchor, right, glyph_yaw] = anchorframe_Current();
-        anim.target_pos  = anchor;
-        anim.yaw_target  = static_cast<float>(glyph_yaw);
-        anim.pitch_target= 0.0;
-        anims_add.push_back(std::move(anim));
+        carry_BeginAnim(std::move(base), /*with_pitch=*/true);
         return true;
     }
 
     // ── Word: same flow, takes Text3dPtr out of ctx.words ───────────
     if (r.kind == PickKind::Word) {
         Text3dPtr word = std::move(ctx.words[r.word_index]);
-        ctx.words.erase(ctx.words.begin() +
-                static_cast<std::ptrdiff_t>(r.word_index));
+        ctx.words.erase(ctx.words.begin() + static_cast<std::ptrdiff_t>(r.word_index));
         if (!word) return false;
 
         step_ledger.picked_up.push_back(word->text);
         word->set_Look(Text3d::color_floor, Text3d::depth_floor);
         TextWall::show_TokenInfo(ctx.wall_texts, word->text, ctx.world->scale());
 
-        WordAnim_Add anim;
-        anim.start_pos = word->pos;
-        anim.yaw_start = word->yaw;
-        anim.word      = std::move(word);
-        anim.t         = 0.0;
-        anim.complete  = false;
-        auto [anchor, right, glyph_yaw] = anchorframe_Current();
-        anim.target_pos = anchor;
-        anim.yaw_target = static_cast<float>(glyph_yaw);
-        anims_add.push_back(std::move(anim));
+        carry_BeginAnim(std::move(word), /*with_pitch=*/false);
         return true;
     }
     return false;
@@ -541,6 +519,7 @@ GameState::update(double dt)
                 case InputCommand::TextStart:     on_TextStart (input); break;
                 case InputCommand::TextCommit:    on_TextCommit(input); break;
                 case InputCommand::TextCancel:    on_TextCancel(input); break;
+                case InputCommand::SandboxRestore:sandbox_Restore();    break;
             }
         }
         if (input.text_mode) {
@@ -840,22 +819,10 @@ GameState::scene_Teardown()
             ctx.scenery.end());
     }
 
-    // Also drop any dynamic-lib spawned objects (boxes in scenery, text in words).
-    if (!mod_scenery.empty()) {
-        std::unordered_set<GameObject*> mod_set(mod_scenery.begin(), mod_scenery.end());
-        ctx.scenery.erase(
-            std::remove_if(ctx.scenery.begin(), ctx.scenery.end(),
-                [&mod_set](const GameObjectPtr& sp){ return mod_set.count(sp.get()) > 0; }),
-            ctx.scenery.end());
-        ctx.words.erase(
-            std::remove_if(ctx.words.begin(), ctx.words.end(),
-                [&mod_set](const Text3dPtr& wp){ return mod_set.count(wp.get()) > 0; }),
-            ctx.words.end());
-    }
+    mod_Clear();
 
     step_scenery.clear();
     step_plates.clear();
-    mod_scenery.clear();
 }
 
 
@@ -1059,21 +1026,27 @@ GameState::level_Advance()
 
 
 void
-GameState::mod_Apply(const std::vector<ModObject>& objs)
+GameState::mod_Clear()
 {
-    // Remove previously-spawned lib objects from the scene.
-    if (!mod_scenery.empty()) {
-        std::unordered_set<GameObject*> set(mod_scenery.begin(), mod_scenery.end());
-        ctx.scenery.erase(
-            std::remove_if(ctx.scenery.begin(), ctx.scenery.end(),
-                [&set](const GameObjectPtr& sp) { return set.count(sp.get()) > 0; }),
-            ctx.scenery.end());
-        ctx.words.erase(
-            std::remove_if(ctx.words.begin(), ctx.words.end(),
-                [&set](const Text3dPtr& wp) { return set.count(wp.get()) > 0; }),
-            ctx.words.end());
-    }
+    if (mod_scenery.empty()) { return; }
+
+    std::unordered_set<GameObject*> set(mod_scenery.begin(), mod_scenery.end());
+    ctx.scenery.erase(
+        std::remove_if(ctx.scenery.begin(), ctx.scenery.end(),
+            [&set](const GameObjectPtr& sp) { return set.count(sp.get()) > 0; }),
+        ctx.scenery.end());
+    ctx.words.erase(
+        std::remove_if(ctx.words.begin(), ctx.words.end(),
+            [&set](const Text3dPtr& wp) { return set.count(wp.get()) > 0; }),
+        ctx.words.end());
     mod_scenery.clear();
+}
+
+
+void
+GameState::mod_Apply(const std::vector<ModObject>& objs)
+{
+    mod_Clear();
 
     // Build fresh objects from the descriptions.
     for (const ModObject& o : objs) {
@@ -1158,28 +1131,12 @@ GameState::sandbox_LoadFileToWall()
 
 
 void GameState::sandbox_Restore() {
+    if (!level_runner || !level_runner->dynamic_lib()) return;
     if (SourceTokens::copy_File(sandbox_BackupPath(), sandbox_CanonicalPath()))
         sandbox_LoadFileToWall();
 }
 
 
-void
-GameState::carry_TypedText(const std::string& s)
-{
-    auto [anchor, right, glyph_yaw] = anchorframe_Current();
-
-    std::istringstream in(s);
-    std::string tok;
-    while (in >> tok) {            // >> splits on any run of whitespace, skips leading/trailing
-        Text3dPtr t = make_Text3d(tok, Text3d::depth_floor, 0.003f,
-                                  anchor, Text3d::color_floor);
-        t->yaw = static_cast<float>(glyph_yaw);
-        t->body.mode = PhysicsMode::Carried;
-        carried.push(std::move(t));
-    }
-}
-
-
 void
 GameState::typing_RebuildPreview(const std::string& s)
 {
@@ -1211,3 +1168,26 @@ GameState::typing_ClearPreview()
     }
     typing_words.clear();
 }
+
+
+void
+GameState::carry_BeginAnim(Text3dPtr word, bool with_pitch)
+{
+    WordAnim_Add anim;
+    anim.start_pos   = word->pos;
+    anim.yaw_start   = word->yaw;
+    if (with_pitch) {
+        anim.pitch_start  = word->pitch;
+        anim.pitch_target = 0.0;
+    }
+    anim.word        = std::move(word);
+    anim.t           = 0.0;
+    anim.complete    = false;
+
+    auto [anchor, right, glyph_yaw] = anchorframe_Current();
+    anim.target_pos  = anchor;
+    anim.yaw_target  = static_cast<float>(glyph_yaw);
+
+    anims_add.push_back(std::move(anim));
+}
+
diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp
index f7d9948..e63f186 100644
--- a/src/states/game_state.hpp
+++ b/src/states/game_state.hpp
@@ -122,6 +122,7 @@ class GameState : public IState
     std::tuple<Vec3, Vec3, double>
          anchorframe_Current() const;
     void carriedline_Update();
+    void carry_BeginAnim(Text3dPtr word, bool with_pitch);
     void pult_DispatchAction(PultAction act);
     void step_Spawn(const std::vector<std::string>& tokens);
     void step_SettingsApply();
@@ -129,12 +130,12 @@ class GameState : public IState
     void infowall_StepdetailsShow();
 
     // dynamic-lib sandbox
+    void mod_Clear();
     void mod_Apply(const std::vector<ModObject>& objs);
-    void sandbox_LoadFileToWall();   // tokenize source_file → editor wall
-    void sandbox_Restore();          // copy backup → canonical, reload wall
+    void sandbox_LoadFileToWall();
+    void sandbox_Restore();
     std::string sandbox_CanonicalPath() const;
     std::string sandbox_BackupPath()    const;
-    void carry_TypedText(const std::string& s);
     void typing_RebuildPreview(const std::string& s);
     void typing_ClearPreview();
 
diff --git a/src/systems/input_system.cpp b/src/systems/input_system.cpp
index 282d78f..19cff5d 100644
--- a/src/systems/input_system.cpp
+++ b/src/systems/input_system.cpp
@@ -48,6 +48,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_E: input.pick     = true; break;
 
                 case SDLK_F1:     game.changeState(cpputils::make_unique<GameState>());  break;
+                case SDLK_F2:     input.commands.push_back(InputCommand::SandboxRestore);break;
                 case SDLK_SPACE:  input.commands.push_back(InputCommand::Jump);          break;
                 case SDLK_R:      input.commands.push_back(InputCommand::Drop);          break;
                 case SDLK_F:      input.commands.push_back(InputCommand::ShootAll);      break;
diff --git a/src/systems/input_system.hpp b/src/systems/input_system.hpp
index 85bba4b..9532c80 100644
--- a/src/systems/input_system.hpp
+++ b/src/systems/input_system.hpp
@@ -15,7 +15,8 @@ enum class InputCommand : uint8_t {
     DebugPrint,
     FreelookStart, FreelookEnd,
     NumberInc, NumberDec,
-    TextStart, TextCommit, TextCancel
+    TextStart, TextCommit, TextCancel,
+    SandboxRestore,
 };
 
 
diff --git a/src/systems/projectile_system.cpp b/src/systems/projectile_system.cpp
index 4457bcd..5aa4b3a 100644
--- a/src/systems/projectile_system.cpp
+++ b/src/systems/projectile_system.cpp
@@ -240,17 +240,26 @@ begin_EditorStick(Projectile& p, Wall::Position hit_pos)
     // where the shot is HEADED on the wall, not the centroid's current height.
     // Up close the centroid hasn't traveled, so its raw y is the spawn height;
     // extrapolating to the plane gives the aimed-at y at any distance.
-    const double plane = wall_plane_coord(hit_pos, WORLD_BOX_SCALE);
+    const double plane  = wall_plane_coord(hit_pos, WORLD_BOX_SCALE);
     const bool   x_wall = (hit_pos == Wall::Position::NegX || hit_pos == Wall::Position::PosX);
-    const double from   = x_wall ? p.launch_origin.x : p.launch_origin.z;
-    const double along  = x_wall ? p.dir.x : p.dir.z;
+
+    // y at the exact moment the flight path crosses the wall plane.
+    // prev_centroid is on the near side, c_pos has just crossed; lerp by how
+    // far along that segment the plane sits. This respects the gravity arc up
+    // to the wall (far shots land where they fall) without the overshoot drift.
+    const Vec3 prev = p.prev_centroid;
+    const Vec3 cur  = c_pos;
+    const double prev_coord = x_wall ? prev.x : prev.z;
+    const double cur_coord  = x_wall ? cur.x  : cur.z;
 
     double c_y;
-    if (SDL_fabs(along) > 1e-6) {
-        const double t = (plane - from) / along;   // launch origin is in front → t > 0
-        c_y = p.launch_origin.y + p.dir.y * t;
+    const double denom = cur_coord - prev_coord;
+    if (SDL_fabs(denom) > 1e-6) {
+        double s = (plane - prev_coord) / denom;   // fraction along the segment
+        s = SDL_clamp(s, 0.0, 1.0);                // stay within the crossing step
+        c_y = prev.y + (cur.y - prev.y) * s;       // interpolated y at the plane
     } else {
-        c_y = c_pos.y;   // shooting parallel to wall; fall back
+        c_y = cur.y;   // moving parallel to wall; fall back
     }
 
     for (Text3dPtr& w : p.words) {
@@ -371,6 +380,7 @@ ProjectileSystem::launch(std::vector<Projectile>&   out,
     proj.words         = std::move(words);
     proj.dir           = fwd;
     proj.launch_origin = centroid;
+    proj.prev_centroid = centroid;
     out.push_back(std::move(proj));
 }
 
@@ -400,12 +410,10 @@ ProjectileSystem::update(std::vector<Projectile>&        projectiles,
 
         if (p.words.empty()) { it = projectiles.erase(it); continue; }
 
-        const Vec3 centroid = Text3d::words_centroid(p.words);
+                const Vec3 centroid = Text3d::words_centroid(p.words);
         bool remove = false;
 
-        if (check_FloorCeiling(p, centroid, ws, results)) {
-            remove = true;
-        }
+        if (check_FloorCeiling(p, centroid, ws, results)) remove = true;
 
         if (!remove) {
             Wall::Position hit_pos{};
@@ -414,13 +422,16 @@ ProjectileSystem::update(std::vector<Projectile>&        projectiles,
                                         wall_text_hits, results);
         }
 
-        // Stalled projectile (lost almost all velocity): drop to floor.
         if (!remove && !p.sticking && length(p.vel) < 1.0) {
             emit_FloorLanding(p, results);
             remove = true;
         }
 
-        if (remove) it = projectiles.erase(it);
-        else        ++it;
+        if (remove) {
+            it = projectiles.erase(it);
+        } else {
+            p.prev_centroid = centroid;
+            ++it;
+        }
     }
 }