sdl_runtime_compiler git · main
SDL3 game for running and compiling code at runtime
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.gzCleanup With Claude
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; + } } }