Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / 6edaa46

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

World scale a bit more global

Christian Immanuel · 2026-06-05 15:12 · 6edaa46281fb80eb68bb86de1caa1eda576a8189

 src/core/main.cpp                  |  2 +-
 src/entities/world.cpp             | 10 +++----
 src/entities/world.hpp             | 17 ++++++-----
 src/levels/level/tutorial.cpp      | 55 ++++++++++++++++++------------------
 src/states/game_state.cpp          | 58 +++++++++++++++++++-------------------
 src/systems/celebration_system.cpp |  7 +++--
 src/systems/compiler_system.cpp    | 10 +++----
 src/systems/compiler_system.hpp    |  7 +++--
 src/systems/hint_system.cpp        |  3 +-
 src/systems/pick_system.cpp        |  6 ++--
 src/systems/program_system.cpp     |  3 +-
 src/systems/program_system.hpp     |  3 +-
 src/systems/projectile_system.cpp  |  3 +-
 src/wall/editor_wall.cpp           | 10 +++----
 14 files changed, 103 insertions(+), 91 deletions(-)

diff --git a/src/core/main.cpp b/src/core/main.cpp
index 62bbf4c..2b77a70 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -9,7 +9,7 @@
 #include "utils/frame_profiler.hpp"
 
 
-int main(void)
+int main()
 {
 
     if (!SDL_Init(SDL_INIT_VIDEO)) {
diff --git a/src/entities/world.cpp b/src/entities/world.cpp
index 4107446..e657f51 100644
--- a/src/entities/world.cpp
+++ b/src/entities/world.cpp
@@ -3,7 +3,7 @@
 World::World()
 {
     // initEdges();
-    _wall_surfaces.generate(_world_scale);
+    _wall_surfaces.generate(get_BoxScale());
 }
 
 
@@ -31,11 +31,11 @@ World::resolve_Player(const Player& player) const
     PlayerCollision out;
     out.vel = player.vel;
 
-    const double min = -_world_scale + player.radius;
-    const double max =  _world_scale - player.radius;
+    const double min = -get_BoxScale() + player.radius;
+    const double max =  get_BoxScale() - player.radius;
 
-    const double minY = player.height - _world_scale;
-    const double maxY = _world_scale;
+    const double minY = player.height - get_BoxScale();
+    const double maxY = get_BoxScale();
 
     out.pos.x = sweepAxis(player.pos.x, min,  max,  out.hit_x);
     out.pos.y = sweepAxis(player.pos.y, minY, maxY, out.hit_y);
diff --git a/src/entities/world.hpp b/src/entities/world.hpp
index 639b1f7..f62aca0 100644
--- a/src/entities/world.hpp
+++ b/src/entities/world.hpp
@@ -15,13 +15,8 @@ struct SweepResult {
     bool hit_z{};
 };
 
-class World
-{
-    float
-    _world_scale       {ConfigGame::world_box_scale};
-
-    WallSurfaces
-    _wall_surfaces;
+class World {
+    WallSurfaces _wall_surfaces;
 public:
     World();
 
@@ -33,4 +28,12 @@ public:
 
     Vec3
     get_SpawnPoint() const;
+
+    template <typename T = float>
+    inline static T
+    get_FloorY()   { return static_cast<T>(-ConfigGame::world_box_scale); }
+
+    template <typename T = float>
+    inline static T
+    get_BoxScale() { return static_cast<T>(ConfigGame::world_box_scale); }
 };
diff --git a/src/levels/level/tutorial.cpp b/src/levels/level/tutorial.cpp
index 42a8c51..973b690 100644
--- a/src/levels/level/tutorial.cpp
+++ b/src/levels/level/tutorial.cpp
@@ -1,3 +1,4 @@
+#include "entities/world.hpp"
 #include "levels/level.hpp"
 #include "utils/config.hpp"
 
@@ -24,12 +25,12 @@ Jump on the plate on top of the platform. )",
                 .scene   = Scene{
                     .words = {},
                     .plates = {
-                        { .pos = { 8.6, static_cast<double>(-ConfigGame::world_box_scale + 2.2f), 0.0 }, .tag = "" },
+                        { .pos = { 8.6, World::get_FloorY<double>() + 2.2, 0.0 }, .tag = "" },
                     },
                     .platforms = {
-                        { .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 } },
+                        { .pos = { 5.0, World::get_FloorY<double>() + 0.4, 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
+                        { .pos = { 6.8, World::get_FloorY<double>() + 1.1, 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
+                        { .pos = { 8.6, World::get_FloorY<double>() + 1.8, 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } },
                     },
                     .ai_seeds  = {},
                 },
@@ -52,10 +53,10 @@ Four walls surround you:
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.2f), 6.0 }, .tag = "" },
+                        { .pos = { 0.0, World::get_FloorY<double>() + 1.2, 6.0 }, .tag = "" },
                     },
                     .platforms = {
-                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 0.6f), 6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
+                        { .pos = { 0.0, World::get_FloorY<double>() + 0.6, 6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
                     },
                     .ai_seeds  = {},
                 },
@@ -76,7 +77,7 @@ Picked words trail in front of you as you move.
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .text = "hello", .pos = { 5.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 0.0 } },
+                        { .text = "hello", .pos = { 5.0, World::get_FloorY<double>() + 1.5, 0.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
@@ -96,9 +97,9 @@ They queue up in the order you grab them.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .text = "keep",    .pos = {  5.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
-                        { .text = "e",       .pos = {  0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
-                        { .text = "pressed", .pos = { -5.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
+                        { .text = "keep",    .pos = {  5.0, World::get_FloorY<double>() + 1.5, -3.0 } },
+                        { .text = "e",       .pos = {  0.0, World::get_FloorY<double>() + 1.5, -3.0 } },
+                        { .text = "pressed", .pos = { -5.0, World::get_FloorY<double>() + 1.5, -3.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
@@ -136,10 +137,10 @@ Carrying several? Tap F to throw the WHOLE line at once.)",
                 .grants  = {},
                 .scene   = {
                     .words = {
-                        { .text = "keep",    .pos = {  5.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
-                        { .text = "left",    .pos = {  3.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
-                        { .text = "click",   .pos = {  0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
-                        { .text = "pressed", .pos = {  2.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
+                        { .text = "keep",    .pos = {  5.0, World::get_FloorY<double>() + 1.5, -3.0 } },
+                        { .text = "left",    .pos = {  3.0, World::get_FloorY<double>() + 1.5, -3.0 } },
+                        { .text = "click",   .pos = {  0.0, World::get_FloorY<double>() + 1.5, -3.0 } },
+                        { .text = "pressed", .pos = {  2.0, World::get_FloorY<double>() + 1.5, -3.0 } },
                     },
                 },
                 .reset_carry  = false,
@@ -153,9 +154,9 @@ Carrying several? Tap F to throw the WHOLE line at once.)",
                 .grants  = {},
                 .scene   = {
                     .words = {
-                        { .text = "f",    .pos = {  1.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
-                        { .text = "for",  .pos = {  2.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -2.0 } },
-                        { .text = "flush",.pos = {  0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), -3.0 } },
+                        { .text = "f",    .pos = {  1.0, World::get_FloorY<double>() + 1.5, -3.0 } },
+                        { .text = "for",  .pos = {  2.0, World::get_FloorY<double>() + 1.5, -2.0 } },
+                        { .text = "flush",.pos = {  0.0, World::get_FloorY<double>() + 1.5, -3.0 } },
                     },
                 },
                 .reset_carry  = false,
@@ -174,18 +175,18 @@ reaches you.)",
                 .grants  = {},
                 .scene   = Scene{
                     .words = {
-                        { .text = "ammo", .pos = { 6.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 6.0 } },
-                        { .text = "ammo", .pos = { 3.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 2.0 } },
-                        { .text = "ammo", .pos = { 6.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 3.0 } },
-                        { .text = "ammo", .pos = { 4.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 6.0 } },
-                        { .text = "ammo", .pos = { 6.0, static_cast<double>(-ConfigGame::world_box_scale + 1.5f), 1.0 } },
+                        { .text = "ammo", .pos = { 6.0, World::get_FloorY<double>() + 1.5, 6.0 } },
+                        { .text = "ammo", .pos = { 3.0, World::get_FloorY<double>() + 1.5, 2.0 } },
+                        { .text = "ammo", .pos = { 6.0, World::get_FloorY<double>() + 1.5, 3.0 } },
+                        { .text = "ammo", .pos = { 4.0, World::get_FloorY<double>() + 1.5, 6.0 } },
+                        { .text = "ammo", .pos = { 6.0, World::get_FloorY<double>() + 1.5, 1.0 } },
                     },
                     .plates    = {},
                     .platforms = {},
                     .ai_seeds  = {
                         {
                             .text           = "danger",
-                            .pos            = { -6.0, static_cast<double>(-ConfigGame::world_box_scale + 2.0f), -6.0 },
+                            .pos            = { -6.0, World::get_FloorY<double>() + 2.0, -6.0 },
                             .initial_state  = Text3dAI::State::Attack,
                             .aggressiveness = 1.0,
                         },
@@ -292,10 +293,10 @@ Have a play, then jump onto the plate to continue.)",
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.2f), -6.0 }, .tag = "" },
+                        { .pos = { 0.0, World::get_FloorY<double>() + 1.2, -6.0 }, .tag = "" },
                     },
                     .platforms = {
-                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 0.6f), -6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
+                        { .pos = { 0.0, World::get_FloorY<double>() + 0.6, -6.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
                     },
                     .ai_seeds  = {},
                 },
@@ -314,10 +315,10 @@ Jump onto the plate to start the first real level:
                 .scene   = Scene{
                     .words  = {},
                     .plates = {
-                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 1.2f), 0.0 }, .tag = "" },
+                        { .pos = { 0.0, World::get_FloorY<double>() + 1.2, 0.0 }, .tag = "" },
                     },
                     .platforms = {
-                        { .pos = { 0.0, static_cast<double>(-ConfigGame::world_box_scale + 0.6f), 0.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
+                        { .pos = { 0.0, World::get_FloorY<double>() + 0.6, 0.0 }, .half_extents = { 1.0, 0.4, 1.0 } },
                     },
                     .ai_seeds = {},
                 },
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 8ae80c7..fe7fd9d 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -45,15 +45,15 @@ GameState::GameState(size_t starting_level)
     _celebration_sys = cpputils::make_unique<CelebrationSystem>();
 
     _floor_sys       = cpputils::make_unique<FloorMandelbrot>  ();
-    _floor_sys->generate(ConfigGame::world_box_scale, -ConfigGame::world_box_scale);
+    _floor_sys->generate(World::get_BoxScale(), World::get_FloorY());
 
-    _wall_labels     = WallLabel::generate_All(ConfigGame::world_box_scale);
+    _wall_labels     = WallLabel::generate_All(World::get_BoxScale());
 
     _word_ai_sys     = cpputils::make_unique<WordAiSystem>();
-    _word_ai_sys->set_WorldScale(ConfigGame::world_box_scale);
+    _word_ai_sys->set_WorldScale(World::get_BoxScale());
 
     _pult_sys        = cpputils::make_unique<PultSystem>  ();
-    _pult_sys->init(_ctx.scenery, _ctx.buttons, ConfigGame::world_box_scale);
+    _pult_sys->init(_ctx.scenery, _ctx.buttons, World::get_BoxScale());
 
     if (starting_level >= Level::level_Count()) starting_level = 0;
     _level_runner_sys = cpputils::make_unique<LevelRunner>(Level::get_Level(starting_level));
@@ -178,7 +178,7 @@ GameState::pult_DispatchAction(PultAction act)
 {
     _step_ledger.buttons_pressed.push_back(act);
 
-    const double floor_y = -static_cast<double>(ConfigGame::world_box_scale);
+    const double floor_y = World::get_FloorY<double>();
 
     switch (act) {
         case PultAction::ScrollEditorUp:
@@ -235,7 +235,7 @@ GameState::on_Pick()
         if (base->type == ObjectType::Text) {
             _step_ledger.picked_up.push_back(base->text);
             base->set_Look(ConfigColor::keep, Text3d::depth_floor);
-            TextWall::show_TokenInfo(_ctx.wall_texts, base->text, ConfigGame::world_box_scale);
+            TextWall::show_TokenInfo(_ctx.wall_texts, base->text, World::get_BoxScale());
         }
         carry_BeginAnim(std::move(base), /*with_pitch=*/true);
         return true;
@@ -249,7 +249,7 @@ GameState::on_Pick()
 
         _step_ledger.picked_up.push_back(word->text);
         word->set_Look(ConfigColor::keep, Text3d::depth_floor);
-        TextWall::show_TokenInfo(_ctx.wall_texts, word->text, ConfigGame::world_box_scale);
+        TextWall::show_TokenInfo(_ctx.wall_texts, word->text, World::get_BoxScale());
 
         carry_BeginAnim(std::move(word), /*with_pitch=*/false);
         return true;
@@ -276,7 +276,7 @@ GameState::on_DropLast()
     _anims_drop.push_back(std::move(drop));
 
     if (!_carried.empty())
-        TextWall::show_TokenInfo(_ctx.wall_texts, _carried.last->word->text, ConfigGame::world_box_scale);
+        TextWall::show_TokenInfo(_ctx.wall_texts, _carried.last->word->text, World::get_BoxScale());
     else
         infowall_StepdetailsShow();
 
@@ -301,14 +301,14 @@ GameState::on_ShootOne()
     single.push_back(std::move(word));
 
     Aim::Result aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
-                                               _ctx.wall_texts, ConfigGame::world_box_scale);
+                                               _ctx.wall_texts, World::get_BoxScale());
     _projectiles_sys->launch(_projectiles, std::move(single /* or all_words */),
                              _ctx.camera->fwd, _ctx.camera->pos, aim);
 
     if (!_carried.empty() && _carried.last && _carried.last->word)
         TextWall::show_TokenInfo(_ctx.wall_texts,
                                  _carried.last->word->text,
-                                 ConfigGame::world_box_scale);
+                                 World::get_BoxScale());
     else infowall_StepdetailsShow();
 
     _step_ledger.shot = true;
@@ -334,7 +334,7 @@ GameState::on_ShootAll()
     _carried = CarriedLine();
     if (!all_words.empty()) {
         Aim::Result aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
-                                                   _ctx.wall_texts, ConfigGame::world_box_scale);
+                                                   _ctx.wall_texts, World::get_BoxScale());
         _projectiles_sys->launch(_projectiles, std::move(all_words),
                                  _ctx.camera->fwd, _ctx.camera->pos, aim);
     }
@@ -355,7 +355,7 @@ void GameState::on_Compile() {
         if (!path.empty() && !stripped.empty())
             SourceTokens::write_File(path, src);
 
-        _mod_loader_sys->build(_ctx.wall_texts, src, ConfigGame::world_box_scale, true);
+        _mod_loader_sys->build(_ctx.wall_texts, src, World::get_BoxScale(), true);
     } else {
         _compiler_sys->compile(_ctx.wall_texts, src);
     }
@@ -365,7 +365,7 @@ void GameState::on_Compile() {
 void
 GameState::on_Run()
 {
-    _program_sys->run(_ctx.wall_texts, ConfigGame::world_box_scale);
+    _program_sys->run(_ctx.wall_texts, World::get_BoxScale());
 }
 
 
@@ -465,7 +465,7 @@ GameState::projectile_FeedProgramInput(ProjectileResult& result)
     if (!line.empty())
         _program_sys->feed_Input(line);
 
-    _program_sys->echo_Words(_ctx.wall_texts, result.words, ConfigGame::world_box_scale);
+    _program_sys->echo_Words(_ctx.wall_texts, result.words, World::get_BoxScale());
 }
 
 
@@ -476,7 +476,7 @@ GameState::step_Spawn(const DynArray<std::string>& tokens)
     static constexpr int   per_row = 8;
     static constexpr float row_gap = 3.0f;
 
-    const double floor_y = -static_cast<double>(ConfigGame::world_box_scale);
+    const double floor_y = World::get_FloorY<double>();
     const double hover_y = floor_y + 1.5;
 
     for (size_t i = 0; i < tokens.size(); ++i) {
@@ -525,7 +525,7 @@ GameState::projectile_LandOnWall(ProjectileResult& result)
         w->body.mode = PhysicsMode::Static;
         w->body.vel  = Vec3{0, 0, 0};
 
-        TextWallPtr wt = TextWall::from_Drop(std::move(w), ConfigGame::world_box_scale);
+        TextWallPtr wt = TextWall::from_Drop(std::move(w), World::get_BoxScale());
         if (wt && wt->text3d && wt->role == Wall::Role::Editor)
             editor_group.push_back(wt->text3d.get());     // collect, don't place yet
         placed.push_back(std::move(wt));
@@ -580,7 +580,7 @@ GameState::walltext_HandleShootDown(ProjectileResult& result,
             Text3dPtr orphan = std::move(wt->text3d);
             orphan->body.mode         = PhysicsMode::Dynamic;
             orphan->body.vel          = { 0.0, 1.5, 0.0 };
-            orphan->body.hover_height = -static_cast<double>(ConfigGame::world_box_scale) + 1.5;
+            orphan->body.hover_height = World::get_FloorY<double>() + 1.5;
             orphan->yaw               = 0.0f;
             orphan->set_Look(ConfigColor::keep, Text3d::depth_floor);
             _ctx.words.push_back(std::move(orphan));
@@ -695,12 +695,12 @@ GameState::update(double dt)
 
         carriedline_Update();
 
-        const double floor_y = -static_cast<double>(ConfigGame::world_box_scale);
+        const double floor_y = World::get_FloorY<double>();
         _word_anim_sys->update(_anims_add, _anims_drop, _carried, _ctx.words, dt, floor_y);
 
-        _pult_sys->update(dt, *_ctx.camera);
+        _pult_sys->  update(dt, *_ctx.camera);
 
-        _plate_sys->update(dt, *_ctx.player, _step_plates);
+        _plate_sys-> update(dt, *_ctx.player, _step_plates);
 
         _editor_wall.update(dt, _ctx.camera->pos);
 
@@ -713,17 +713,17 @@ GameState::update(double dt)
         particle_colliders.reserve(_ctx.scenery.size());
         for (const GameObjectPtr& s : _ctx.scenery)
             if (s) particle_colliders.push_back(s.get());
-        _particle_sys->update(dt, particle_colliders, ConfigGame::world_box_scale);
+        _particle_sys->update(dt, particle_colliders, World::get_BoxScale());
     }
 
     { FP_ZONE("update._compiler_sys");
-        _compiler_sys->update(_ctx.wall_texts, ConfigGame::world_box_scale);
+        _compiler_sys->update(_ctx.wall_texts);
         if (_compiler_sys->last_compile_ok) {
             _compiler_sys->last_compile_ok = false;
             _step_ledger.compile_ok = true;
         }
 
-        _mod_loader_sys->update(_ctx.wall_texts, ConfigGame::world_box_scale);
+        _mod_loader_sys->update(_ctx.wall_texts, World::get_BoxScale());
         if (_mod_loader_sys->last_build_ok) {
             _mod_loader_sys->last_build_ok = false;
             mod_Apply(_mod_loader_sys->load_Objects());
@@ -732,7 +732,7 @@ GameState::update(double dt)
     }
 
     { FP_ZONE("update._program_sys");
-        _program_sys->update(_ctx.wall_texts, ConfigGame::world_box_scale);
+        _program_sys->update(_ctx.wall_texts, World::get_BoxScale());
     }
 
     { FP_ZONE("update.goals");
@@ -870,7 +870,7 @@ GameState::infowall_StepdetailsShow()
     const std::string& details = _level_runner_sys->current().details;
     if (details.empty()) return;
 
-    const float      world_scale    = ConfigGame::world_box_scale;
+    const float      world_scale    = World::get_BoxScale();
     Wall::Transform  wall_transform = Wall::get_Transform(Wall::Role::Info, world_scale);
 
     constexpr size_t MAX_INFO_LINE_CHARS = 92;
@@ -991,7 +991,7 @@ GameState::substep(double dt)
     }
 
     { FP_ZONE("subst.phys_objects");
-        _physics_sys->update_Objects(colliders, dt, 25.0, ConfigGame::world_box_scale);
+        _physics_sys->update_Objects(colliders, dt, 25.0, World::get_BoxScale());
     }
 
     { FP_ZONE("subst.word_ai");
@@ -1061,7 +1061,7 @@ GameState::substep(double dt)
     DynArray<ProjectileResult> proj_results;
     { FP_ZONE("subst.projectile_sys");
         _projectiles_sys->update(_projectiles, proj_results,
-                                 wall_text_hits, dt, ConfigGame::world_box_scale,
+                                 wall_text_hits, dt, World::get_BoxScale(),
                                  _program_sys->is_Running());
     }
 
@@ -1273,7 +1273,7 @@ GameState::sandbox_LoadFileToWall()
             [](const TextWallPtr& wt){ return wt->role == Wall::Role::Editor; }),
         _ctx.wall_texts.end());
 
-    const float ws = ConfigGame::world_box_scale;
+    const float ws = World::get_BoxScale();
     const double x_wall = -static_cast<double>(ws) + 0.05;   // NegX wall surface
     Wall::Transform tf = Wall::get_Transform(Wall::Role::Editor, ws);
 
@@ -1415,7 +1415,7 @@ GameState::conn_Submit()
 void
 GameState::conn_RebuildPreview()
 {
-    Wall::Transform tf = Wall::get_Transform(Wall::Role::Program, ConfigGame::world_box_scale);
+    Wall::Transform tf = Wall::get_Transform(Wall::Role::Program, World::get_BoxScale());
     Vec3 pos = tf.position;
     pos.y = ProgramSystem::start_y;
 
diff --git a/src/systems/celebration_system.cpp b/src/systems/celebration_system.cpp
index c70a418..3dbd98f 100644
--- a/src/systems/celebration_system.cpp
+++ b/src/systems/celebration_system.cpp
@@ -1,5 +1,6 @@
 #include "celebration_system.hpp"
 #include "entities/camera.hpp"
+#include "entities/world.hpp"
 #include "systems/particle_system.hpp"
 
 void
@@ -15,8 +16,8 @@ CelebrationSystem::start(const Camera& cam, double& glyph_yaw)
         cam.pos.z + cam.fwd.z * 10.0,
     };
 
-    if (banner_pos.y < static_cast<double>(-ConfigGame::world_box_scale+1.0f) )
-        banner_pos.y = -ConfigGame::world_box_scale+1.0f;
+    if (banner_pos.y < World::get_FloorY<double>()+1.0) 
+        banner_pos.y = World::get_FloorY<double>()+1.0;
 
     _banner = make_Text3d("Success",
                          Text3d::depth_floor * 2.0f,
@@ -44,7 +45,7 @@ CelebrationSystem::tick(double dt, ParticleSystem& particle_sys)
         _t <  _duration - 0.3) {
         // Random position across the play area, biased to upper half so
         // bursts read as airborne fireworks not floor_sys explosions.
-        const double ws = ConfigGame::world_box_scale;
+        const double ws = World::get_BoxScale<double>();
         const double margin = 3.0;
         auto rnd = []() {
             return Random::unit();
diff --git a/src/systems/compiler_system.cpp b/src/systems/compiler_system.cpp
index d5e040d..5b2f4c3 100644
--- a/src/systems/compiler_system.cpp
+++ b/src/systems/compiler_system.cpp
@@ -37,7 +37,7 @@ CompilerSystem::compile(TextWallList& texts, const std::string& src)
 
 
 void
-CompilerSystem::update(TextWallList& texts, float world_scale)
+CompilerSystem::update(TextWallList& texts)
 {
     if (!_pipe) return;
 
@@ -54,14 +54,14 @@ CompilerSystem::update(TextWallList& texts, float world_scale)
             pclose(_pipe);
             _pipe = nullptr;
             if (_count == 0)
-                push_Line(texts, "OK", world_scale);
+                push_Line(texts, "OK");
             return;
         }
         std::string line(buf);
         // Strip trailing newline
         if (!line.empty() && line.back() == '\n') line.pop_back();
         if (!line.empty())
-            push_Line(texts, line, world_scale);
+            push_Line(texts, line);
 
         FD_ZERO(&set); FD_SET(fd, &set);
     }
@@ -69,7 +69,7 @@ CompilerSystem::update(TextWallList& texts, float world_scale)
 
 
 void
-CompilerSystem::push_Line(TextWallList& texts, const std::string& line, float world_scale)
+CompilerSystem::push_Line(TextWallList& texts, const std::string& line)
 {
     if (line == "OK") last_compile_ok = true;
 
@@ -79,7 +79,7 @@ CompilerSystem::push_Line(TextWallList& texts, const std::string& line, float wo
         if (wt->role == Wall::Role::Compiler)
             wt->text3d->pos.y += static_cast<double>(line_height);
 
-    Wall::Transform tf = Wall::get_Transform(Wall::Role::Compiler, world_scale);
+    Wall::Transform tf = Wall::get_Transform(Wall::Role::Compiler, World::get_BoxScale());
 
     Vec3 pos = tf.position;
     pos.y = start_y;
diff --git a/src/systems/compiler_system.hpp b/src/systems/compiler_system.hpp
index 1fd64fe..20f1a40 100644
--- a/src/systems/compiler_system.hpp
+++ b/src/systems/compiler_system.hpp
@@ -2,6 +2,7 @@
 #include <string>
 #include <cstdio>
 
+#include "entities/world.hpp"
 #include "utils/globals.hpp"
 #include "wall/text_wall.hpp"
 
@@ -12,14 +13,14 @@ private:
     FILE* _pipe  {nullptr};
     int   _count {0};
 
-    void push_Line(TextWallList&, const std::string& line, float world_scale);
+    void push_Line(TextWallList&, const std::string& line);
 public:
     static inline float line_height = 0.4f;
-    static inline float start_y     = -ConfigGame::world_box_scale+1;
+    static inline float start_y     = World::get_FloorY()+1;
 
     bool last_compile_ok{false};
 
-    void update    (TextWallList&, float world_scale);
+    void update    (TextWallList&);
     void compile   (TextWallList&, const std::string& src);
     bool is_Running() const { return _pipe != nullptr; }
 };
diff --git a/src/systems/hint_system.cpp b/src/systems/hint_system.cpp
index 798ff02..68eac4e 100644
--- a/src/systems/hint_system.cpp
+++ b/src/systems/hint_system.cpp
@@ -1,9 +1,10 @@
+#include "entities/world.hpp"
 #include "systems/hint_system.hpp"
 #include "utils/config.hpp"
 
 HintSystem::HintSystem()
 {
-    _label.set_Centre(Vec3{ 0.0, static_cast<double>(-ConfigGame::world_box_scale) + 2.5, 0.0 });
+    _label.set_Centre(Vec3{ 0.0, World::get_FloorY<double>() + 2.5, 0.0 });
     _label.set_Billboard(true);
     _label.set_Style(ConfigColor::yellow, Text3d::depth_wall, 0.003f);
     _label.set_Collidable(true);
diff --git a/src/systems/pick_system.cpp b/src/systems/pick_system.cpp
index 84c7c06..180d50f 100644
--- a/src/systems/pick_system.cpp
+++ b/src/systems/pick_system.cpp
@@ -1,6 +1,8 @@
-#include "pick_system.hpp"
 #include <SDL3/SDL.h>
 
+#include "pick_system.hpp"
+#include "entities/world.hpp"
+
 namespace PickSystem
 {
 
@@ -76,7 +78,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>(ConfigGame::world_box_scale);
+    const double floor_y = World::get_FloorY();
 
     PickResult result;
     double best_angle = MAX_ANGLE;   // any actual hit must beat this
diff --git a/src/systems/program_system.cpp b/src/systems/program_system.cpp
index 5982cd2..ca58d75 100644
--- a/src/systems/program_system.cpp
+++ b/src/systems/program_system.cpp
@@ -7,6 +7,7 @@
 #include <sys/select.h>
 #include <sys/wait.h>
 
+#include "entities/world.hpp"
 #include "systems/program_system.hpp"
 #include "text_3d/text_3d.hpp"
 #include "utils/ascii.hpp"
@@ -168,7 +169,7 @@ ProgramSystem::feed_Input(const std::string& text)
 void
 ProgramSystem::echo_Input(TextWallList& texts, const std::string& line)
 {
-    push_Line(texts, line, ConfigGame::world_box_scale);   // scrolls up, lands at the prompt row
+    push_Line(texts, line, World::get_BoxScale());   // scrolls up, lands at the prompt row
 }
 
 
diff --git a/src/systems/program_system.hpp b/src/systems/program_system.hpp
index 34b964b..7fa4a1a 100644
--- a/src/systems/program_system.hpp
+++ b/src/systems/program_system.hpp
@@ -2,6 +2,7 @@
 #include <string>
 #include <sys/types.h>   // pid_t
 
+#include "entities/world.hpp"
 #include "utils/globals.hpp"
 #include "wall/text_wall.hpp"
 
@@ -23,7 +24,7 @@ private:
     void close_Child();         // close pipes, reap (or kill) the child
 public:
     static inline float line_height = 0.4f;
-    static inline float start_y     = -ConfigGame::world_box_scale + 1;
+    static inline float start_y     = World::get_FloorY() + 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 cc1ebae..b753f10 100644
--- a/src/systems/projectile_system.cpp
+++ b/src/systems/projectile_system.cpp
@@ -1,5 +1,6 @@
 #include <algorithm>
 
+#include "entities/world.hpp"
 #include "projectile_system.hpp"
 #include "systems/collision.hpp"
 #include "utils/config.hpp"
@@ -241,7 +242,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, ConfigGame::world_box_scale);
+    const double plane  = wall_plane_coord(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 ? p.cam_origin.x : p.cam_origin.z;
diff --git a/src/wall/editor_wall.cpp b/src/wall/editor_wall.cpp
index 8a95917..7bc8d03 100644
--- a/src/wall/editor_wall.cpp
+++ b/src/wall/editor_wall.cpp
@@ -1,10 +1,10 @@
-#include "editor_wall.hpp"
-
 #include <SDL3/SDL.h>
 #include <algorithm>
 #include <string>
 
+#include "editor_wall.hpp"
 #include "entities/camera.hpp"
+#include "entities/world.hpp"
 #include "rendering/gl_renderer.hpp"
 
 
@@ -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, ConfigGame::world_box_scale);
+    const Wall::Transform tf = Wall::get_Transform(Wall::Role::Editor, World::get_BoxScale());
 
     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   = ConfigGame::world_box_scale;
+    const double ws   = World::get_BoxScale();
     const double EDGE = 0.3;
     double left_col_z  = code_left  + ConfigWall::line_number_gap;
     double right_col_z = code_right - ConfigWall::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 = ConfigGame::world_box_scale;
+    const float ws = World::get_BoxScale();
     const float x_front = -ws + 0.02f;
 
     constexpr float nx = 1.0f, ny = 0.0f, nz = 0.0f;