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

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

Namespace configs

Christian Immanuel · 2026-06-03 07:31 · 12d3edde6d34ff7315d3cde5c489e10f1b3cd723

 src/entities/camera.hpp            |  6 ++---
 src/entities/player.cpp            |  2 +-
 src/entities/world.hpp             |  4 +--
 src/levels/level_list.cpp          | 36 +++++++++++++--------------
 src/rendering/gl_renderer.cpp      |  4 +--
 src/states/game_state.cpp          | 10 ++++----
 src/states/menu_state.cpp          |  8 +++---
 src/systems/celebration_system.cpp |  6 ++---
 src/systems/compiler_system.hpp    |  2 +-
 src/systems/hint_system.cpp        |  2 +-
 src/systems/physics_system.cpp     |  8 +++---
 src/systems/pick_system.cpp        |  2 +-
 src/systems/program_system.hpp     |  2 +-
 src/systems/projectile_system.cpp  | 28 ++++++++++-----------
 src/systems/word_ai_system.cpp     |  4 +--
 src/systems/word_anim.cpp          |  2 +-
 src/utils/config.hpp               | 50 ++++++++++++++++++++++++--------------
 src/wall/editor_wall.cpp           |  6 ++---
 src/wall/text_wall.cpp             |  2 +-
 src/wall/wall.hpp                  |  8 +++---
 20 files changed, 103 insertions(+), 89 deletions(-)

diff --git a/src/entities/camera.hpp b/src/entities/camera.hpp
index 7c58afc..e32de85 100644
--- a/src/entities/camera.hpp
+++ b/src/entities/camera.hpp
@@ -21,8 +21,8 @@ struct Camera {
         yaw   = player.yaw;
         pitch = player.pitch;
 
-        yaw_r   = BINRAD * yaw;
-        pitch_r = BINRAD * pitch;
+        yaw_r   = ConfigPhysics::binrad * yaw;
+        pitch_r = ConfigPhysics::binrad * pitch;
         fwd = {
            -SDL_sin(yaw_r) * SDL_cos(pitch_r),
             SDL_sin(pitch_r),
@@ -38,7 +38,7 @@ struct Camera {
 
 inline void buildViewMatrix(const Camera& cam, double out[9])
 {
-    double bin   = BINRAD;
+    double bin   = ConfigPhysics::binrad;
 
     double yaw   = bin * cam.yaw;
     double pitch = bin * cam.pitch;
diff --git a/src/entities/player.cpp b/src/entities/player.cpp
index 61c08c9..6f198c0 100644
--- a/src/entities/player.cpp
+++ b/src/entities/player.cpp
@@ -42,7 +42,7 @@ Player::add_Look(int dx, int dy)
 void
 Player::compute_Movement(const FrameInput& input)
 {
-    double rad = (double)yaw * BINRAD;
+    double rad = (double)yaw * ConfigPhysics::binrad;
     double c = SDL_cos(rad);
     double s = SDL_sin(rad);
 
diff --git a/src/entities/world.hpp b/src/entities/world.hpp
index 4e21969..4cb62be 100644
--- a/src/entities/world.hpp
+++ b/src/entities/world.hpp
@@ -22,13 +22,13 @@ struct SweepResult {
 class World
 {
     static inline constexpr int
-    _map_box_scale     {static_cast<int>(WORLD_BOX_SCALE)};
+    _map_box_scale     {static_cast<int>(ConfigGame::world_box_scale)};
 
     static inline constexpr int
     _map_box_edges_len {12 + _map_box_scale * 2};
 
     float
-    _world_scale       {WORLD_BOX_SCALE};
+    _world_scale       {ConfigGame::world_box_scale};
 
     WallSurfaces
     _wall_surfaces;
diff --git a/src/levels/level_list.cpp b/src/levels/level_list.cpp
index b68a362..e20696c 100644
--- a/src/levels/level_list.cpp
+++ b/src/levels/level_list.cpp
@@ -12,7 +12,7 @@
 //    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.
+//  * floor_y == -ConfigGame::world_box_scale. Heights are written as -ConfigGame::world_box_scale + dy.
 // ─────────────────────────────────────────────────────────────────────────────
 
 static const DynArray<Level> levels =
@@ -36,7 +36,7 @@ Step onto it to continue.)",
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE), 6.0 }, .tag = "" },
+                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale), 6.0 }, .tag = "" },
                     },
                     .platforms = {},
                     .ai_seeds  = {},
@@ -61,12 +61,12 @@ up high — you'll need this jump to reach them.)",
                 .scene   = Scene{
                     .words = {},
                     .plates = {
-                        { .pos = { 8.6, static_cast<double>(-WORLD_BOX_SCALE + 2.2f), 0.0 }, .tag = "" },
+                        { .pos = { 8.6, static_cast<double>(-ConfigGame::world_box_scale + 2.2f), 0.0 }, .tag = "" },
                     },
                     .platforms = {
-                        { .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 } },
+                        { .pos = { 5.0, static_cast<double>(-ConfigGame::world_box_scale + 0.4f), 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
+                        { .pos = { 6.8, static_cast<double>(-ConfigGame::world_box_scale + 1.1f), 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
+                        { .pos = { 8.6, static_cast<double>(-ConfigGame::world_box_scale + 1.8f), 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
                     },
                     .ai_seeds  = {},
                 },
@@ -93,10 +93,10 @@ When you've seen them all, jump onto the plate.)",
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 1.2f), 6.0 }, .tag = "" },
+                        { .pos = { 0.0, static_cast<double>(-ConfigGame::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 } },
+                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 0.6f), 6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
                     },
                     .ai_seeds  = {},
                 },
@@ -115,7 +115,7 @@ Picked words trail in front of you as you move.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .text = "hello", .pos = { 5.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), 0.0 } },
+                        { .text = "hello", .pos = { 5.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 0.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
@@ -136,9 +136,9 @@ They queue up in the order you grab them.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .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 } },
+                        { .text = "press", .pos = {  5.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
+                        { .text = "any",   .pos = {  0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
+                        { .text = "key",   .pos = { -5.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
@@ -194,14 +194,14 @@ reaches you.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .text = "ammo", .pos = { 6.0, static_cast<double>(-WORLD_BOX_SCALE + 1.5f), 6.0 } },
+                        { .text = "ammo", .pos = { 6.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 6.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
                     .ai_seeds  = {
                         {
                             .text           = "danger",
-                            .pos            = { -6.0, static_cast<double>(-WORLD_BOX_SCALE + 2.0f), -6.0 },
+                            .pos            = { -6.0, static_cast<double>(-ConfigGame::world_box_scale + 2.0f), -6.0 },
                             .initial_state  = Text3dAI::State::Attack,
                             .aggressiveness = 1.0,
                         },
@@ -314,10 +314,10 @@ Have a play, then jump onto the plate to continue.)",
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 1.2f), -6.0 }, .tag = "" },
+                        { .pos = { 0.0, static_cast<double>(-ConfigGame::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 } },
+                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 0.6f), -6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
                     },
                     .ai_seeds  = {},
                 },
@@ -337,10 +337,10 @@ Jump onto the plate to start the first real level:
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 1.2f), 0.0 }, .tag = "" },
+                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.2f), 0.0 }, .tag = "" },
                     },
                     .platforms = {
-                        { .pos = { 0.0, static_cast<double>(-WORLD_BOX_SCALE + 0.6f), 0.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
+                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 0.6f), 0.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
                     },
                     .ai_seeds = {},
                 },
diff --git a/src/rendering/gl_renderer.cpp b/src/rendering/gl_renderer.cpp
index 272a02b..0b761c9 100644
--- a/src/rendering/gl_renderer.cpp
+++ b/src/rendering/gl_renderer.cpp
@@ -340,8 +340,8 @@ Mat4 Mat4::mul(const Mat4& a, const Mat4& b)
 
 Mat4 buildViewMat4(const Camera& cam)
 {
-    double yaw   = BINRAD * (double)cam.yaw;
-    double pitch = BINRAD * (double)cam.pitch;
+    double yaw   = ConfigPhysics::binrad * (double)cam.yaw;
+    double pitch = ConfigPhysics::binrad * (double)cam.pitch;
 
     double cy = SDL_cos(yaw),   sy = SDL_sin(yaw);
     double cp = SDL_cos(pitch), sp = SDL_sin(pitch);
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 18ae582..4cefc80 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -617,14 +617,14 @@ GameState::update(double dt)
 
     // why do i still have that MAX_SUBSTEPS and not whatever frames we get
     int steps = 0;
-    while (_accumulator >= dt && steps < MAX_SUBSTEPS) {
+    while (_accumulator >= dt && steps < ConfigGame::max_substeps) {
         FP_ZONE("update.fixed_substep");
         substep(dt);
         _accumulator -= dt;
         ++steps;
     }
 
-    _accumulator = SDL_min(_accumulator, dt * MAX_SUBSTEPS);
+    _accumulator = SDL_min(_accumulator, dt * ConfigGame::max_substeps);
     _alpha       = _accumulator / dt;
 
     { FP_ZONE("update.post_step");
@@ -693,8 +693,8 @@ GameState::render(SDL_Window& window, GlRenderer& renderer)
     SDL_GetWindowSizeInPixels(&window, &w, &h);
 
     { FP_ZONE("render.light_setup");
-        const double yaw_r   = BINRAD * static_cast<double>(_ctx.camera->yaw);
-        const double pitch_r = BINRAD * static_cast<double>(_ctx.camera->pitch);
+        const double yaw_r   = ConfigPhysics::binrad * static_cast<double>(_ctx.camera->yaw);
+        const double pitch_r = ConfigPhysics::binrad * static_cast<double>(_ctx.camera->pitch);
         const double cy = SDL_cos(yaw_r),   sy = SDL_sin(yaw_r);
         const double cp = SDL_cos(pitch_r), sp = SDL_sin(pitch_r);
         _ctx.light->flashlight_pos = _ctx.camera->pos;
@@ -1083,7 +1083,7 @@ GameState::level_TryAdvance()
     GoalContext gctx = goal_ContextBuild();
     if (!goal_Satisfied(_level_runner_sys->current().goal, gctx))
         return false;
-    if (_step_age < SETTLE_TIME_FOR_STEP)
+    if (_step_age < ConfigLevel::settle_time_for_step)
         return false;
     if (!_level_runner_sys || _level_runner_sys->complete())
         return false;
diff --git a/src/states/menu_state.cpp b/src/states/menu_state.cpp
index 1072299..0bcad66 100644
--- a/src/states/menu_state.cpp
+++ b/src/states/menu_state.cpp
@@ -52,7 +52,7 @@ MenuState::yaw_FromRadians(double rad)
 double
 MenuState::yaw_ToRadians(uint32_t yaw)
 {
-    return static_cast<double>(yaw) * (BINRAD);
+    return static_cast<double>(yaw) * (ConfigPhysics::binrad);
 }
 
 
@@ -249,7 +249,7 @@ size_t
 MenuState::pick_FromCrosshair() const
 {
     const double yaw   = yaw_ToRadians(_camera->yaw);
-    const double pitch = static_cast<double>(_camera->pitch) * BINRAD;
+    const double pitch = static_cast<double>(_camera->pitch) * ConfigPhysics::binrad;
     const double cy = SDL_cos(yaw),  sy = SDL_sin(yaw);
     const double cp = SDL_cos(pitch), sp = SDL_sin(pitch);
 
@@ -367,8 +367,8 @@ MenuState::render(SDL_Window& window, GlRenderer& renderer)
 
     // Update flashlight: pos = _camera, dir = where the _camera is looking.
     {
-        const double yaw_r   = BINRAD * static_cast<double>(_camera->yaw);
-        const double pitch_r = BINRAD * static_cast<double>(_camera->pitch);
+        const double yaw_r   = ConfigPhysics::binrad * static_cast<double>(_camera->yaw);
+        const double pitch_r = ConfigPhysics::binrad * static_cast<double>(_camera->pitch);
         const double cy = SDL_cos(yaw_r),   sy = SDL_sin(yaw_r);
         const double cp = SDL_cos(pitch_r), sp = SDL_sin(pitch_r);
 
diff --git a/src/systems/celebration_system.cpp b/src/systems/celebration_system.cpp
index 2066b8b..8e64754 100644
--- a/src/systems/celebration_system.cpp
+++ b/src/systems/celebration_system.cpp
@@ -15,8 +15,8 @@ CelebrationSystem::start(const Camera& cam, double& glyph_yaw)
         cam.pos.z + cam.fwd.z * 10.0,
     };
 
-    if (banner_pos.y < static_cast<double>(-WORLD_BOX_SCALE+1.0f) )
-        banner_pos.y = -WORLD_BOX_SCALE+1.0f;
+    if (banner_pos.y < static_cast<double>(-ConfigGame::world_box_scale+1.0f) )
+        banner_pos.y = -ConfigGame::world_box_scale+1.0f;
 
     _banner = make_Text3d("Success",
                          Text3d::depth_floor * 2.0f,
@@ -45,7 +45,7 @@ CelebrationSystem::tick(double dt, ParticleSystem& particle_sys)
     {
         // Random position across the play area, biased to upper half so
         // bursts read as airborne fireworks not floor_sys explosions.
-        const double ws = WORLD_BOX_SCALE;
+        const double ws = ConfigGame::world_box_scale;
         const double margin = 3.0;
         auto rnd = []() {
             return static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
diff --git a/src/systems/compiler_system.hpp b/src/systems/compiler_system.hpp
index aa7e405..1fd64fe 100644
--- a/src/systems/compiler_system.hpp
+++ b/src/systems/compiler_system.hpp
@@ -15,7 +15,7 @@ private:
     void push_Line(TextWallList&, const std::string& line, float world_scale);
 public:
     static inline float line_height = 0.4f;
-    static inline float start_y     = -WORLD_BOX_SCALE+1;
+    static inline float start_y     = -ConfigGame::world_box_scale+1;
 
     bool last_compile_ok{false};
 
diff --git a/src/systems/hint_system.cpp b/src/systems/hint_system.cpp
index 6ab4e3b..53fa313 100644
--- a/src/systems/hint_system.cpp
+++ b/src/systems/hint_system.cpp
@@ -3,7 +3,7 @@
 
 HintSystem::HintSystem()
 {
-    _label.set_Centre(Vec3{ 0.0, static_cast<double>(-WORLD_BOX_SCALE) + 2.5, 0.0 });
+    _label.set_Centre(Vec3{ 0.0, static_cast<double>(-ConfigGame::world_box_scale) + 2.5, 0.0 });
     _label.set_Billboard(true);
     _label.set_Style(Text3d::color_yellow, Text3d::depth_wall, 0.003f);
     _label.set_Collidable(true);
diff --git a/src/systems/physics_system.cpp b/src/systems/physics_system.cpp
index cf59310..9817f12 100644
--- a/src/systems/physics_system.cpp
+++ b/src/systems/physics_system.cpp
@@ -202,9 +202,9 @@ PhysicsSystem::update_Objects(const DynArray<GameObject*>& objects,
                 continue;
 
             if (obj->body.mode == PhysicsMode::Hover) {
-                obj->body.hover_phase += WORD_HOVER_SPEED * dt;
+                obj->body.hover_phase += ConfigText3d::hover_speed * dt;
                 obj->pos.y = obj->body.hover_height +
-                             SDL_sin(obj->body.hover_phase) * WORD_HOVER_AMP;
+                             SDL_sin(obj->body.hover_phase) * ConfigText3d::hover_amp;
                 continue;
             }
 
@@ -295,10 +295,10 @@ PhysicsSystem::update_Projectiles(DynArray<Projectile>& projectiles, double dt)
         // Speed-scaled gravity
         double speed = length(p.vel);
         double grav_scale = 1.0 + speed * 0.02;
-        p.vel.y -= PROJ_GRAV * grav_scale * dt;
+        p.vel.y -= ConfigPhysics::proj_grav * grav_scale * dt;
 
         // Exponential horizontal drag
-        double drag_factor  = SDL_pow(AIR_DRAG, dt * 60.0);
+        double drag_factor  = SDL_pow(ConfigPhysics::air_drag, dt * 60.0);
         p.vel.x *= drag_factor;
         p.vel.z *= drag_factor;
 
diff --git a/src/systems/pick_system.cpp b/src/systems/pick_system.cpp
index 885a75e..84c7c06 100644
--- a/src/systems/pick_system.cpp
+++ b/src/systems/pick_system.cpp
@@ -76,7 +76,7 @@ pick(const Camera& camera,
     // Floor plane. Words/wall-text scrolled (or fallen) below the floor are
     // hidden under it but still angle-pickable, so exclude anything whose
     // centre has dropped beneath the floor surface.
-    const double floor_y = -static_cast<double>(WORLD_BOX_SCALE);
+    const double floor_y = -static_cast<double>(ConfigGame::world_box_scale);
 
     PickResult result;
     double best_angle = MAX_ANGLE;   // any actual hit must beat this
diff --git a/src/systems/program_system.hpp b/src/systems/program_system.hpp
index b550221..fc09e0c 100644
--- a/src/systems/program_system.hpp
+++ b/src/systems/program_system.hpp
@@ -17,7 +17,7 @@ private:
     void push_Line(TextWallList&, const std::string& line, float world_scale);
 public:
     static inline float line_height = 0.4f;
-    static inline float start_y     = -WORLD_BOX_SCALE + 1;
+    static inline float start_y     = -ConfigGame::world_box_scale + 1;
 
     void run    (TextWallList&, float world_scale);
     void update (TextWallList&, float world_scale);
diff --git a/src/systems/projectile_system.cpp b/src/systems/projectile_system.cpp
index 334d2e3..8de0c7e 100644
--- a/src/systems/projectile_system.cpp
+++ b/src/systems/projectile_system.cpp
@@ -149,20 +149,20 @@ detect_WallHit(const Projectile& p, const Vec3& centroid, double dt, double ws,
     const double next_x = centroid.x + p.vel.x * dt;
     const double next_z = centroid.z + p.vel.z * dt;
 
-    if ((centroid.x > -ws + STICK_DIST && next_x <= -ws + STICK_DIST) ||
-        (centroid.x <= -ws + STICK_DIST && p.vel.x < 0.0))
+    if ((centroid.x > -ws + ConfigPhysics::stick_dist && next_x <= -ws + ConfigPhysics::stick_dist) ||
+        (centroid.x <= -ws + ConfigPhysics::stick_dist && p.vel.x < 0.0))
         { out_pos = Wall::Position::NegX; return true; }
 
-    if ((centroid.x <  ws - STICK_DIST && next_x >=  ws - STICK_DIST) ||
-        (centroid.x >= ws - STICK_DIST && p.vel.x > 0.0))
+    if ((centroid.x <  ws - ConfigPhysics::stick_dist && next_x >=  ws - ConfigPhysics::stick_dist) ||
+        (centroid.x >= ws - ConfigPhysics::stick_dist && p.vel.x > 0.0))
         { out_pos = Wall::Position::PosX; return true; }
 
-    if ((centroid.z > -ws + STICK_DIST && next_z <= -ws + STICK_DIST) ||
-        (centroid.z <= -ws + STICK_DIST && p.vel.z < 0.0))
+    if ((centroid.z > -ws + ConfigPhysics::stick_dist && next_z <= -ws + ConfigPhysics::stick_dist) ||
+        (centroid.z <= -ws + ConfigPhysics::stick_dist && p.vel.z < 0.0))
         { out_pos = Wall::Position::NegZ; return true; }
 
-    if ((centroid.z <  ws - STICK_DIST && next_z >=  ws - STICK_DIST) ||
-        (centroid.z >= ws - STICK_DIST && p.vel.z > 0.0))
+    if ((centroid.z <  ws - ConfigPhysics::stick_dist && next_z >=  ws - ConfigPhysics::stick_dist) ||
+        (centroid.z >= ws - ConfigPhysics::stick_dist && p.vel.z > 0.0))
         { out_pos = Wall::Position::PosZ; return true; }
 
     return false;
@@ -240,7 +240,7 @@ begin_EditorStick(Projectile& p, Wall::Position hit_pos)
     // line-width off the aim, which is what dragged the merge sideways. This
     // also gives the aimed-at height with no gravity soak at any distance.
     const Vec3   c_pos  = Text3d::words_centroid(p.words);
-    const double plane  = wall_plane_coord(hit_pos, WORLD_BOX_SCALE);
+    const double plane  = wall_plane_coord(hit_pos, ConfigGame::world_box_scale);
     const bool   x_wall = (hit_pos == Wall::Position::NegX || hit_pos == Wall::Position::PosX);
 
     const double cam_coord = x_wall ? p.cam_origin.x : p.cam_origin.z;
@@ -292,14 +292,14 @@ bounce_OffWall(Projectile& p, Wall::Position hit_pos, double ws,
         p.vel.x = bounce_velocity(p.vel.x, 0.70);
         for (Text3dPtr& w : p.words) if (w)
             w->pos.x = (hit_pos == Wall::Position::PosX)
-                ? ( ws - STICK_DIST - 0.05)
-                : (-ws + STICK_DIST + 0.05);
+                ? ( ws - ConfigPhysics::stick_dist - 0.05)
+                : (-ws + ConfigPhysics::stick_dist + 0.05);
     } else {
         p.vel.z = bounce_velocity(p.vel.z, 0.70);
         for (Text3dPtr& w : p.words) if (w)
             w->pos.z = (hit_pos == Wall::Position::PosZ)
-                ? ( ws - STICK_DIST - 0.05)
-                : (-ws + STICK_DIST + 0.05);
+                ? ( ws - ConfigPhysics::stick_dist - 0.05)
+                : (-ws + ConfigPhysics::stick_dist + 0.05);
     }
     return false;
 }
@@ -375,7 +375,7 @@ ProjectileSystem::launch(DynArray<Projectile>&   out,
     }
 
     Projectile proj;
-    proj.vel           = fwd * SHOOT_SPEED;
+    proj.vel           = fwd * ConfigPhysics::shoot_speed;
     proj.yaw           = yaw;
     proj.bounce_state  = 0;
     proj.words         = std::move(words);
diff --git a/src/systems/word_ai_system.cpp b/src/systems/word_ai_system.cpp
index b2e7790..2827931 100644
--- a/src/systems/word_ai_system.cpp
+++ b/src/systems/word_ai_system.cpp
@@ -258,7 +258,7 @@ WordAiSystem::update_Flee(Text3d& w, const Player& p, DynArray<Text3dPtr>& relea
         if (w.ai.state_timer > FLEE_ESCAPE_TIME) {
             // Got away. If carrying loot, roll for drop-vs-keep.
             if (!w.ai.stolen.empty()) {
-                if (random_unit() < AI_DROP_LOOT_PROBABILITY) {
+                if (random_unit() < ConfigAi::drop_loot_probability) {
                     release_StolenWords(w, released);
                 }
                 // Whether we dropped or kept, come back for another attack.
@@ -437,5 +437,5 @@ WordAiSystem::update_StolenTrail(Text3d& w, double dt)
     }
 
     // Tick hover_phase so bob/sway animate even when attacker is paused
-    w.body.hover_phase += WORD_HOVER_SPEED * dt;
+    w.body.hover_phase += ConfigText3d::hover_speed * dt;
 }
diff --git a/src/systems/word_anim.cpp b/src/systems/word_anim.cpp
index e224443..ef0a165 100644
--- a/src/systems/word_anim.cpp
+++ b/src/systems/word_anim.cpp
@@ -55,7 +55,7 @@ WordAnimSystem::update(DynArray<WordAnim_Add>&    anims_add,
         if (drop.complete) continue;
 
         // Integrate: gravity + position update.
-        drop.word->body.vel.y -= PROJ_GRAV * dt;
+        drop.word->body.vel.y -= ConfigPhysics::proj_grav * dt;
         drop.word->pos.x += drop.word->body.vel.x * dt;
         drop.word->pos.y += drop.word->body.vel.y * dt;
         drop.word->pos.z += drop.word->body.vel.z * dt;
diff --git a/src/utils/config.hpp b/src/utils/config.hpp
index 131ee41..2009b06 100644
--- a/src/utils/config.hpp
+++ b/src/utils/config.hpp
@@ -2,40 +2,54 @@
 
 #include <SDL3/SDL.h>
 
-static constexpr double
-FIXED_DT = 1.0 / 60.0;
-
+namespace ConfigGame
+{
 static constexpr int
-MAX_SUBSTEPS = 4;
-
+max_substeps    = 4;
 static constexpr float 
-WORLD_BOX_SCALE = 16.f;
+world_box_scale = 16.f;
+}
 
-// PHYSICS
 
+namespace ConfigPhysics
+{
 static constexpr double
-BINRAD = SDL_PI_D / 2147483648.0;
-
+binrad    = SDL_PI_D / 2147483648.0;
 static constexpr double
-PROJ_GRAV = 15.0;
+proj_grav = 15.0;
 static constexpr double
-AIR_DRAG  = 0.993;
+air_drag  = 0.993;
 
 static constexpr double
-SHOOT_SPEED = 34.0;
+shoot_speed = 34.0;
 static constexpr double
-STICK_DIST  =  0.2;
+stick_dist  =  0.2;
+}
+
 
+namespace ConfigText3d
+{
 static constexpr double
-WORD_HOVER_AMP   = 0.3;
+hover_amp   = 0.3;
 static constexpr double
-WORD_HOVER_SPEED = 1.5;
+hover_speed = 1.5;
+}
+
 
+namespace ConfigAi
+{
 static constexpr double
-AI_DROP_LOOT_PROBABILITY = 0.35;
+drop_loot_probability = 0.35;
+}
 
+namespace ConfigLevel
+{
 static constexpr double
-SETTLE_TIME_FOR_STEP = 0.25;   // 250ms grace period
+settle_time_for_step = 0.25;   // 250ms grace period
+}
 
+namespace ConfigWall
+{
 static constexpr float
-WALL_TEXT_OFFSET = 0.1f;   // distance from wall surface
+text_offset = 0.1f;   // distance from wall surface
+}
diff --git a/src/wall/editor_wall.cpp b/src/wall/editor_wall.cpp
index dc5d080..70bc2e0 100644
--- a/src/wall/editor_wall.cpp
+++ b/src/wall/editor_wall.cpp
@@ -251,7 +251,7 @@ EditorWall::update(double dt, const Vec3& eye_pos)
     std::sort(order.begin(), order.end(),
         [&](size_t a, size_t b){ return _lines[a].y > _lines[b].y; });
 
-    const Wall::Transform tf = Wall::get_Transform(Wall::Role::Editor, WORLD_BOX_SCALE);
+    const Wall::Transform tf = Wall::get_Transform(Wall::Role::Editor, ConfigGame::world_box_scale);
 
     if (order.empty()) return;
 
@@ -297,7 +297,7 @@ EditorWall::update(double dt, const Vec3& eye_pos)
     // The wall spans z in [-ws, +ws]; keep a small margin in from each edge.
     // A left number occupies [left_col_z, left_col_z + 2*lh] (outer edge = +z);
     // a right number occupies [right_col_z - 2*rh, right_col_z] (outer = -z).
-    const double ws   = WORLD_BOX_SCALE;
+    const double ws   = ConfigGame::world_box_scale;
     const double EDGE = 0.3;
     double left_col_z  = code_left  + LINE_NUMBER_GAP;
     double right_col_z = code_right - LINE_NUMBER_GAP;
@@ -381,7 +381,7 @@ EditorWall::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
     verts  .reserve(_lines.size() * 4 * 9);
     indices.reserve(_lines.size() * 6);
 
-    const float ws = WORLD_BOX_SCALE;
+    const float ws = ConfigGame::world_box_scale;
     const float x_front = -ws + 0.02f;
 
     constexpr float nx = 1.0f, ny = 0.0f, nz = 0.0f;
diff --git a/src/wall/text_wall.cpp b/src/wall/text_wall.cpp
index 4d7bdf2..6c5cbb0 100644
--- a/src/wall/text_wall.cpp
+++ b/src/wall/text_wall.cpp
@@ -26,7 +26,7 @@ TextWall::from_Drop(Text3dPtr src, float world_scale)
     // world X — we offset by he.z to sit flush against the wall.
     // For Z walls the word faces straight on, so local Z (depth) points along
     // world Z — same he.z offset.
-    double wo = static_cast<double>(WALL_TEXT_OFFSET);
+    double wo = static_cast<double>(ConfigWall::text_offset);
     if (dPX <= dNX && dPX <= dPZ && dPX <= dNZ) { wall_pos = Wall::Position::PosX; src->pos.x =  ws - he.z - wo; yaw = -SDL_PI_F * 0.5f; }
     else if (dNX <= dPZ && dNX <= dNZ)          { wall_pos = Wall::Position::NegX; src->pos.x = -ws + he.z + wo; yaw =  SDL_PI_F * 0.5f; }
     else if (dPZ <= dNZ)                        { wall_pos = Wall::Position::PosZ; src->pos.z =  ws - he.z - wo; yaw =  SDL_PI_F; }
diff --git a/src/wall/wall.hpp b/src/wall/wall.hpp
index dfe8628..95d86c5 100644
--- a/src/wall/wall.hpp
+++ b/src/wall/wall.hpp
@@ -54,20 +54,20 @@ namespace Wall {
         switch (position_of(role)) {
             case Position::PosX:
                 return { Position::PosX,
-                         { world_scale - WALL_TEXT_OFFSET, 0.0, 0.0 },
+                         { world_scale - ConfigWall::text_offset, 0.0, 0.0 },
                          -SDL_PI_F * 0.5f };
             case Position::NegX:
                 return { Position::NegX,
-                         { -world_scale + WALL_TEXT_OFFSET, 0.0, 0.0 },
+                         { -world_scale + ConfigWall::text_offset, 0.0, 0.0 },
                          SDL_PI_F * 0.5f };
             case Position::PosZ:
                 return { Position::PosZ,
-                         { 0.0, 0.0, world_scale - WALL_TEXT_OFFSET },
+                         { 0.0, 0.0, world_scale - ConfigWall::text_offset },
                          SDL_PI_F };
             case Position::NegZ:
             default:
                 return { Position::NegZ,
-                         { 0.0, 0.0, -world_scale + WALL_TEXT_OFFSET },
+                         { 0.0, 0.0, -world_scale + ConfigWall::text_offset },
                          0.0f };
         }
     }