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

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

Fix shootdown whitespace And Start fixing shoot down token by angel bug

Christian Immanuel · 2026-06-26 16:21 · 943354662f907f0cf03dbcb119de381925f53792

 src/states/game_state.cpp         |  4 +--
 src/systems/input_system.cpp      | 16 +++++------
 src/systems/input_system.hpp      |  6 ++---
 src/systems/projectile_system.cpp | 57 +++++++++------------------------------
 src/systems/projectile_system.hpp |  2 --
 src/text/handler.cpp              | 25 +++++++++++++++--
 src/utils/config.hpp              |  1 -
 7 files changed, 48 insertions(+), 63 deletions(-)

diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 454d3ac..45d1b0c 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -337,7 +337,7 @@ GameState::on_ShootOne()
     Aim::Result aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
                                              _text_walls, World::get_BoxScale());
     _projectiles_sys->launch(_projectiles, std::move(single),
-                             _ctx.camera->fwd, _ctx.camera->pos, aim);
+                             _ctx.camera->fwd, aim);
 
     if (!_carried.empty() && _carried.last && _carried.last->word) {
         Wall::show_TokenInfo(_wall_info, _carried.last->word->text);
@@ -373,7 +373,7 @@ GameState::on_ShootAll()
     aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
                                  _text_walls, World::get_BoxScale());
     _projectiles_sys->launch(_projectiles, std::move(all_words),
-                             _ctx.camera->fwd, _ctx.camera->pos, aim);
+                             _ctx.camera->fwd, aim);
     _level_director->show_Details(_ctx);
     _level_director->record_Shot ();
 }
diff --git a/src/systems/input_system.cpp b/src/systems/input_system.cpp
index 01dab3f..07ab859 100644
--- a/src/systems/input_system.cpp
+++ b/src/systems/input_system.cpp
@@ -37,11 +37,10 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
         }
         case SDL_EVENT_KEY_DOWN: {
             SDL_Keycode sym = e.key.key;
+            const bool ctrl = (e.key.mod & SDL_KMOD_CTRL);
 
-            if (input.capture_vim && !input.normal_keys) {
-                const bool ctrl = (e.key.mod & SDL_KMOD_CTRL);
-
-                if (ctrl) { input.normal_keys = true; break; }
+            if (input.capture_vim && !input.vim_normal_keys) {
+                if (ctrl) { input.vim_normal_keys = true; break; }
 
                      if (sym == SDLK_RETURN)    input.vim_keys.push_back(-1);
                 else if (sym == SDLK_ESCAPE)    input.vim_keys.push_back(-2);
@@ -50,7 +49,6 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 break;
             }
             if (input.capture_typing) {
-                const bool ctrl = (e.key.mod & SDL_KMOD_CTRL);
                 if (ctrl) {
                     switch (sym) {
                         case SDLK_W: input.forward  = true; break;
@@ -90,7 +88,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_E: input.pick     = true; break;
                 case SDLK_X: if (!e.key.repeat)
                                  input.commands.push_back(InputCommand::FocusToggle);
-                             break;
+                                                    break;
 
                 case SDLK_F1:     game.change_State(cpputils::make_unique<GameState>()); break;
                 case SDLK_F2:     input.commands.push_back(InputCommand::SandboxRestore);break;
@@ -103,7 +101,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_T:      input.commands.push_back(InputCommand::TypingStart);   break;
                 case SDLK_LSHIFT:
                 case SDLK_RSHIFT: input.commands.push_back(InputCommand::FreelookStart); break;
-                case SDLK_ESCAPE: game.change_State(cpputils::make_unique<MenuState>()); break;
+                case SDLK_ESCAPE: if (ctrl) game.change_State(cpputils::make_unique<MenuState>()); break;
                 case SDLK_PLUS:
                     if (e.key.mod & SDL_KMOD_CTRL)
                                 { input.commands.push_back(InputCommand::NumberInc); }   break;
@@ -124,7 +122,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_D: input.right    = false; break;
                 case SDLK_E: input.pick     = false; break;
 
-                case SDLK_LCTRL: input.normal_keys = false; break;
+                case SDLK_LCTRL: input.vim_normal_keys = false; break;
 
                 case SDLK_LSHIFT:
                 case SDLK_RSHIFT:
@@ -136,7 +134,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
         }
 
         case SDL_EVENT_TEXT_INPUT:
-            if (input.capture_vim && !input.normal_keys) {
+            if (input.capture_vim && !input.vim_normal_keys) {
                 for (const char* p = e.text.text; *p; ++p)
                     input.vim_keys.push_back((unsigned char)*p);
             }
diff --git a/src/systems/input_system.hpp b/src/systems/input_system.hpp
index 53ed353..7fbabcb 100644
--- a/src/systems/input_system.hpp
+++ b/src/systems/input_system.hpp
@@ -40,10 +40,10 @@ struct FrameInput
     bool pick     = false;
     bool shoot    = false;// X held: focus the faced wall
 
-    bool normal_keys    = false;
+    bool vim_normal_keys = false;
 
-    bool capture_typing = false;
-    bool capture_vim    = false;
+    bool capture_typing  = false;
+    bool capture_vim     = false;
 
     bool typing_mode   = false;
     bool typing_commit = false;
diff --git a/src/systems/projectile_system.cpp b/src/systems/projectile_system.cpp
index bb90fcd..58f7c75 100644
--- a/src/systems/projectile_system.cpp
+++ b/src/systems/projectile_system.cpp
@@ -238,26 +238,13 @@ beginEditorStick(Projectile& projectile, Wall::Position hit_pos)
     const double dot  = cr_x * wt_x + cr_z * wt_z;
     const double sign = (dot >= 0.0) ? 1.0 : -1.0;
 
-    const Vec3   c_pos  = Text::Token::Data::words_centroid(projectile.words);
-    const double plane  = wallPlaneCoord(hit_pos, World::get_BoxScale());
-    const bool   x_wall = (hit_pos == Wall::Position::NegX || hit_pos == Wall::Position::PosX);
-
-    const double cam_coord = x_wall ? projectile.cam_origin.x : projectile.cam_origin.z;
-    const double dir_coord = x_wall ? projectile.dir.x        : projectile.dir.z;
-
-    Vec3 aim;
-    if (SDL_fabs(dir_coord) > 1e-6) {
-        const double t = (plane - cam_coord) / dir_coord;
-        aim = Vec3{ projectile.cam_origin.x + projectile.dir.x * t,
-                    projectile.cam_origin.y + projectile.dir.y * t,
-                    projectile.cam_origin.z + projectile.dir.z * t };
-    }
-    else {
-        aim = c_pos;
-    }
-
-    const double c_tan = wallTangent(hit_pos, aim.x, aim.z);
-    const double c_y   = aim.y;
+    // Stick where the phrase actually is — its current centroid — NOT where the
+    // original launch ray would cross the wall. The launch ray ignores the gravity
+    // arc and is meaningless after a bounce (it would teleport the word, which is
+    // why bounced shots used to respawn near the world centre).
+    const Vec3   c_pos = Text::Token::Data::words_centroid(projectile.words);
+    const double c_tan = wallTangent(hit_pos, c_pos.x, c_pos.z);
+    const double c_y   = c_pos.y;
 
     for (Text::TokenPtr& w : projectile.words) {
         if (!w || w->type != ObjectType::Text) continue;
@@ -334,23 +321,6 @@ bounceOffWall(Projectile& projectile, Wall::Position hit_pos, double ws,
 }
 
 
-static double
-editorAimY(const Projectile& projectile, double plane_x)
-{
-    if (SDL_fabs(projectile.dir.x) <= 1e-6) return Text::Token::Data::words_centroid(projectile.words).y;
-    const double param_dist = (plane_x - projectile.cam_origin.x) / projectile.dir.x;
-    return projectile.cam_origin.y + projectile.dir.y * param_dist;
-}
-
-static double
-editorAimZ(const Projectile& projectile, double plane_x)
-{
-    if (SDL_fabs(projectile.dir.x) <= 1e-6) return Text::Token::Data::words_centroid(projectile.words).z;
-    const double param_dist = (plane_x - projectile.cam_origin.x) / projectile.dir.x;
-    return projectile.cam_origin.z + projectile.dir.z * param_dist;
-}
-
-
 static bool
 handleWallHit(Projectile& projectile, const Vec3& centroid, Wall::Position hit_pos,
                double dt,double ws,
@@ -364,9 +334,10 @@ handleWallHit(Projectile& projectile, const Vec3& centroid, Wall::Position hit_p
     }
 
     const bool   editor = (Wall::role_Of(hit_pos) == Wall::Role::Editor);
-    const double plane  = wallPlaneCoord(hit_pos, ws);
-    const double aim_y  = editor ? editorAimY(projectile, plane) : 0.0;
-    const double aim_z  = editor ? editorAimZ(projectile, plane) : 0.0;
+    // Aim at where the phrase ACTUALLY is (its current centroid), so the gravity
+    // arc and any prior bounce are respected. The old launch-ray aim ignored both.
+    const double aim_y  = editor ? centroid.y : 0.0;
+    const double aim_z  = editor ? centroid.z : 0.0;
 
     size_t hit_idx = 0;
     if (projectile.words.size() == 1 &&
@@ -395,7 +366,6 @@ void
 ProjectileSystem::launch(DynArray<Projectile>& out,
                          DynArray<Text::TokenPtr> words,
                          const Vec3& fwd,
-                         const Vec3& eye,
                          const Aim::Result& aim)
 {
     if (words.empty()) return;
@@ -443,7 +413,6 @@ ProjectileSystem::launch(DynArray<Projectile>& out,
     proj.words         = std::move(words);
     proj.dir           = fwd;
     proj.launch_origin = centroid;
-    proj.cam_origin    = eye;
     proj.prev_centroid = centroid;
 
     if (aim.track) {
@@ -457,8 +426,8 @@ ProjectileSystem::launch(DynArray<Projectile>& out,
 
 
 void
-ProjectileSystem::update(DynArray<Projectile>&        projectiles,
-                         DynArray<ProjectileResult>&  results,
+ProjectileSystem::update(DynArray<Projectile>& projectiles,
+                         DynArray<ProjectileResult>& results,
                          const DynArray<WallTextHitTest>& wall_text_hits,
                          double dt, bool program_running)
 {
diff --git a/src/systems/projectile_system.hpp b/src/systems/projectile_system.hpp
index 2859e71..c57a3f9 100644
--- a/src/systems/projectile_system.hpp
+++ b/src/systems/projectile_system.hpp
@@ -31,7 +31,6 @@ struct Projectile
     Vec3 dir{};
     Vec3 prev_centroid{};
     Vec3 launch_origin{};
-    Vec3 cam_origin   {};
 
     Vec3 track_local  {};
     Vec3 track_last   {};
@@ -82,7 +81,6 @@ struct ProjectileSystem
     void launch(DynArray<Projectile>& out,
                 DynArray<Text::TokenPtr> words,
                 const Vec3& fwd,
-                const Vec3& eye,
                 const Aim::Result& aim);
 
     void update(DynArray<Projectile>&        projectiles,
diff --git a/src/text/handler.cpp b/src/text/handler.cpp
index c3be681..e519633 100644
--- a/src/text/handler.cpp
+++ b/src/text/handler.cpp
@@ -576,14 +576,35 @@ Handler::classify_Impact(const Vec3& impact) const
     const double vcf = (_code_left_z - impact.z) / _char_w;   // fractional visual column
 
     int vcol = 0; size_t b = 0; size_t k = 0;
+
+    // Split exactly like relayout_Mono: each space/tab is one cell, and a
+    // non-whitespace run is lexically split into word / operator-group /
+    // single-punctuation sub-tokens. This keeps `k` in lock-step with
+    // line.tokens, so a DropToken's token_idx points at the token actually
+    // aimed at — previously a run was one coarse cell, so token_idx skewed and
+    // the caller detached the wrong token (often whitespace, esp. in sandbox).
+    auto is_word = [](char cc) {
+        return (cc >= 'a' && cc <= 'z') || (cc >= 'A' && cc <= 'Z')
+            || (cc >= '0' && cc <= '9') || cc == '_';
+    };
+    auto is_op = [](char cc) {
+        for (const char* o = "+-*/%=<>!&|^~.:?"; *o; ++o) if (*o == cc) return true;
+        return false;
+    };
+
     while (b < s.size()) {
         const char c = s[b];
         int    b0 = static_cast<int>(b), b1, vc = vcol, vs;
         bool   ws;
         if (c == ' ')       { b1 = b0 + 1; vs = 1;            ws = true;  ++b; }
         else if (c == '\t') { b1 = b0 + 1; vs = 4 - (vcol%4); ws = true;  ++b; }
-        else { size_t s0 = b; while (b < s.size() && s[b] != ' ' && s[b] != '\t') ++b;
-               b1 = static_cast<int>(b); vs = static_cast<int>(b - s0); ws = false; }
+        else {
+            const size_t s0 = b;
+            if      (is_word(c)) { while (b < s.size() && is_word(s[b])) ++b; }
+            else if (is_op(c))   { while (b < s.size() && is_op(s[b]))   ++b; }
+            else                 { ++b; }                 // single bracket / ; , quote …
+            b1 = static_cast<int>(b); vs = static_cast<int>(b - s0); ws = false;
+        }
 
         if (vcf >= vc && vcf < vc + vs) {
             if (ws) { hit.kind = HitKind::Insert; hit.col = b1; return hit; }  // split: word after the space
diff --git a/src/utils/config.hpp b/src/utils/config.hpp
index 4b6c66d..c61dc7f 100644
--- a/src/utils/config.hpp
+++ b/src/utils/config.hpp
@@ -12,7 +12,6 @@ Config
 
 namespace Game
 {
-static constexpr int   max_substeps    = 4;
 static constexpr float world_box_scale = 16.f;
 }