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.gzFix carriedline linting With Claude
src/levels/director.cpp | 24 +++++++++------ src/levels/director.hpp | 3 +- src/states/game_state.cpp | 70 ++++++++++-------------------------------- src/states/menu_state.cpp | 2 ++ src/systems/carry_lex.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++ src/systems/carry_lex.hpp | 9 ++++++ src/systems/word_ai_system.cpp | 17 +++++++--- src/text/handler.cpp | 10 ++---- src/text/handler.hpp | 4 +++ src/text/mesh.cpp | 2 +- src/text/token.cpp | 2 +- src/utils/frame_profiler.hpp | 10 +++--- src/wall/wall.cpp | 5 +++ 13 files changed, 146 insertions(+), 82 deletions(-) diff --git a/src/levels/director.cpp b/src/levels/director.cpp index dae0cf8..4e90f73 100644 --- a/src/levels/director.cpp +++ b/src/levels/director.cpp @@ -107,6 +107,7 @@ Director::setup_Scene(GameContext& ctx, const Scene::Data& scene) t->ai.state = Text::Token::Ai::State::Attack; t->body.mode = PhysicsMode::AiHover; t->set_Color(ConfigColor::attack); + t->set_ColorSource(Text::ColorSource::Static); t->ai.state_timer = 0.0; t->ai.phase_timer = 0.5; break; @@ -114,6 +115,7 @@ Director::setup_Scene(GameContext& ctx, const Scene::Data& scene) t->ai.state = Text::Token::Ai::State::Flee; t->body.mode = PhysicsMode::AiHover; t->set_Color(ConfigColor::flee); + t->set_ColorSource(Text::ColorSource::Static); break; case Text::Token::Ai::State::Idle: case Text::Token::Ai::State::Stunned: @@ -145,24 +147,28 @@ Director::teardown_Scene(GameContext& ctx) void Director::show_Details(GameContext& ctx) const { - SPIKE("direktor.show_Details"); if (!ctx.info_wall) return; Text::Handler& info = *ctx.info_wall; - info.clear(); - if (_runner->is_Complete()) return; + if (_runner->is_Complete()) { info.clear(); return; } + + const Level::Step* step = &_runner->get_Current(); + const size_t tag = std::hash<const void*>{}(step) | 1; + if (info.view_Is(tag)) return; + + info.clear(); - const Scene::Data* key = &_runner->get_Current().scene; + const Scene::Data* key = &step->scene; if (key != _detail_key) { - const std::string& details = _runner->get_Current().details; - _detail_lines = details.empty() - ? DynArray<std::string>{} - : Text::wrap_String(details, 92); + const std::string& details = step->details; + _detail_lines = details.empty() ? DynArray<std::string>{} + : Text::wrap_String(details, 92); _detail_key = key; } - for (const std::string& line : _detail_lines) if (!line.empty()) info.append_Line(line); + + info.set_View(tag); } diff --git a/src/levels/director.hpp b/src/levels/director.hpp index 7a9593b..ad883f3 100644 --- a/src/levels/director.hpp +++ b/src/levels/director.hpp @@ -58,7 +58,8 @@ class Director StepLedger _ledger; DynArray<StepPlate> _step_plates; DynArray<GameObject*> _step_scenery; - mutable const Scene::Data* _detail_key { nullptr }; + mutable const Level::Step* _detail_shown_for { nullptr }; + mutable const Scene::Data* _detail_key { nullptr }; mutable DynArray<std::string> _detail_lines; void spawn_Grants (GameContext& ctx); diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp index fbc5ec7..96482cc 100644 --- a/src/states/game_state.cpp +++ b/src/states/game_state.cpp @@ -9,6 +9,7 @@ #include "game_state.hpp" #include "rendering/gl_renderer.hpp" #include "systems/aim.hpp" +#include "systems/carry_lex.hpp" #include "systems/celebration_system.hpp" #include "systems/cpp_highlight_system.hpp" #include "systems/game_mod_abi.h" @@ -22,8 +23,6 @@ #include "wall/label.hpp" #include "wall/wall.hpp" -static void lex_CarriedLine(CarriedLine& carry); - GameState::GameState(size_t starting_level) { @@ -217,7 +216,6 @@ GameState::on_Pick() _level_director-> record_PickedUp(base->text); base->set_Look (ConfigColor::keep, Text::Token::depth_floor); Wall::show_TokenInfo (_info, base->text); - lex_CarriedLine (_carried); carry_BeginAnim (std::move(base), true); return true; } @@ -230,7 +228,6 @@ GameState::on_Pick() _level_director->record_PickedUp(word->text); word->set_Look (ConfigColor::keep, Text::Token::depth_floor); Wall::show_TokenInfo(_info, word->text); - lex_CarriedLine (_carried); carry_BeginAnim (std::move(word), false); return true; } @@ -245,6 +242,7 @@ GameState::on_DropLast() Text::TokenPtr word = _carried.pop_back(); if (!word) return; + revert_ToSingle(word.get()); infowall_Clear(); @@ -272,6 +270,7 @@ GameState::on_ShootOne() Text::TokenPtr word = _carried.pop_back(); if (!word) return false; + revert_ToSingle(word.get()); infowall_Clear(); @@ -309,24 +308,22 @@ GameState::on_ShootAll() DynArray<Text::TokenPtr> all_words; for (CarriedWordNode* node : _carried.nodes()) - if (node->word) + if (node->word) { + revert_ToSingle (node->word.get()); all_words.push_back(std::move(node->word)); + } _carried = CarriedLine(); Aim::Result aim; if (all_words.empty()) return; - { FP_ZONE("dispatch.ShootAll.aim"); - aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery, - _text_walls, World::get_BoxScale()); } - { FP_ZONE("dispatch.ShootAll.launch"); - _projectiles_sys->launch(_projectiles, std::move(all_words), - _ctx.camera->fwd, _ctx.camera->pos, aim); } - { FP_ZONE("dispatch.ShootAll.details"); - _level_director->show_Details(_ctx); } - { FP_ZONE("dispatch.ShootAll.record"); - _level_director->record_Shot (); } + 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); + _level_director->show_Details(_ctx); + _level_director->record_Shot (); } @@ -683,6 +680,7 @@ GameState::update(double dt, const GlRenderer* gl) carriedline_Update(); _word_anim_sys->update(_anims_add, _anims_drop, _carried, _ctx.words, dt); + lex_CarriedLine(_carried); _pult_sys-> update(dt, *_ctx.camera); _plate_sys-> update(dt, *_ctx.player, _level_director->get_StepPlates()); @@ -947,7 +945,8 @@ GameState::update_WorldSimulation(double dt) word->ai.state = Text::Token::Ai::State::Stunned; word->ai.state_timer = 0.0; word->body.vel = Vec3{0, 0, 0}; - word->set_Color(ConfigColor::stunned); + word->set_Color (ConfigColor::stunned); + word->set_ColorSource(Text::ColorSource::Static); } } } @@ -1116,6 +1115,7 @@ GameState::typing_ClearPreview() } _typing_words.clear(); + lex_CarriedLine(_carried); } @@ -1207,41 +1207,3 @@ GameState::on_EditorScroll(int8_t dt) if (dt > 0) _editor.scroll( ConfigWall::scroll_step); else _editor.scroll(-ConfigWall::scroll_step); } - - - - -void -lex_CarriedLine(CarriedLine& carry) -{ - std::string src; - DynArray<Text::Token::Data*> toks; - for (CarriedWordNode* n : carry.nodes()) { - if (!n->word) continue; - Text::Token::Data* t = n->word.get(); - if (!src.empty()) src += ' '; - t->byte_begin = src.size(); - src += t->text; - t->byte_end = src.size(); - toks.push_back(t); - } - if (toks.empty()) return; - - DynArray<Text::HiSpan> spans = Text::CppHighlight::classify(src); - - for (Text::Token::Data* t : toks) { - size_t best = 0; - Text::Token::Type type = Text::Token::Type::Identifier; - bool found = false; - for (const Text::HiSpan& s : spans) { - size_t lo = (t->byte_begin > s.byte_begin) ? t->byte_begin : s.byte_begin; - size_t hi = (t->byte_end < s.byte_end) ? t->byte_end : s.byte_end; - if (lo < hi && (hi - lo) > best) { best = hi - lo; type = s.type; found = true; } - } - t->set_ColorSource(Text::ColorSource::Lexed); - if (found) t->set_Context(Text::Token::get_Color(type)); - else t->clear_Context(); - } -} - - diff --git a/src/states/menu_state.cpp b/src/states/menu_state.cpp index c921d6e..7628d9d 100644 --- a/src/states/menu_state.cpp +++ b/src/states/menu_state.cpp @@ -119,6 +119,7 @@ MenuState::build_Items() _items[i].slot_pos, color); _items[i].text3d->body.mode = PhysicsMode::Static; + _items[i].text3d->set_ColorSource(Text::ColorSource::Static); } } @@ -296,6 +297,7 @@ MenuState::update(double dt, const GlRenderer*) const size_t hover = pick_FromCrosshair(); if (hover != static_cast<size_t>(-1) && hover != _selected) { _items[_selected].text3d->set_Color(COLOR_NORMAL); + _items[hover].text3d->set_Color(COLOR_SELECTED); _selected = hover; } diff --git a/src/systems/carry_lex.cpp b/src/systems/carry_lex.cpp new file mode 100644 index 0000000..2147532 --- /dev/null +++ b/src/systems/carry_lex.cpp @@ -0,0 +1,70 @@ +#include "carry_lex.hpp" + +#include <SDL3/SDL.h> +#include <string> + +#include "systems/carry_system.hpp" +#include "text/text.hpp" +#include "text/token.hpp" + + +// Re-lex the entire carried line as ONE source string, then assign each word a +// context colour by mapping highlight spans onto its byte range. Mirrors +// Handler::apply_Highlight, but synchronous and scoped to a single joined line. +void +lex_CarriedLine(CarriedLine& carry) +{ + // 1) Build the joined source + per-token byte spans, in reading order. + // NOTE: nodes() is front->back. If the carried line reads right-to-left + // on screen (get_SlotPosition lays out from `last` with decreasing + // offset), switch this to reverse_nodes() so "int x" lexes as "int x" + // and not "x int". Verify against the on-screen order once. + std::string src; + DynArray<Text::Token::Data*> toks; + + for (CarriedWordNode* n : carry.nodes()) { + if (!n->word) continue; + Text::Token::Data* t = n->word.get(); + if (!src.empty()) src += ' '; + t->byte_begin = src.size(); + src += t->text; + t->byte_end = src.size(); + toks.push_back(t); + } + if (toks.empty()) return; + + // 2) Classify the whole line as one context. + DynArray<Text::HiSpan> spans = Text::CppHighlight::classify(src); + + // 3) Map spans -> tokens by maximum byte overlap. + for (Text::Token::Data* t : toks) { + size_t best = 0; + Text::Token::Type type = Text::Token::Type::Identifier; + bool found = false; + + for (const Text::HiSpan& s : spans) { + size_t lo = (t->byte_begin > s.byte_begin) ? t->byte_begin : s.byte_begin; + size_t hi = (t->byte_end < s.byte_end) ? t->byte_end : s.byte_end; + if (lo < hi) { + size_t ov = hi - lo; + if (ov > best) { best = ov; type = s.type; found = true; } + } + } + + t->set_ColorSource(Text::ColorSource::Lexed); // carried line is a context + if (found) t->set_Context(Text::Token::get_Color(type)); + else t->clear_Context(); // no span -> single fallback + } +} + + +// A word that leaves the carried line is no longer in a context: drop the +// context flag and select its lone (single) colour, the same colour it shows on +// the floor or as a projectile. +void +revert_ToSingle(Text::Token::Data* word) +{ + if (!word) return; + word->clear_Context(); + word->set_ColorSource(Text::ColorSource::Lint); +} diff --git a/src/systems/carry_lex.hpp b/src/systems/carry_lex.hpp new file mode 100644 index 0000000..4dd36b7 --- /dev/null +++ b/src/systems/carry_lex.hpp @@ -0,0 +1,9 @@ +#pragma once + + +struct CarriedLine; + +namespace Text { namespace Token { struct Data; } } + +void lex_CarriedLine (CarriedLine& carry); +void revert_ToSingle (Text::Token::Data* word); diff --git a/src/systems/word_ai_system.cpp b/src/systems/word_ai_system.cpp index 8574e9c..67271cc 100644 --- a/src/systems/word_ai_system.cpp +++ b/src/systems/word_ai_system.cpp @@ -46,12 +46,14 @@ WordAiSystem::update(DynArray<Text::TokenPtr>& words, if (!s) continue; s->body.mode = PhysicsMode::Dynamic; s->body.vel = w->body.vel * 0.5; - s->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(s->text))); + s->recolor (); + s->set_ColorSource(Text::ColorSource::Lint); released.push_back(std::move(s)); } w->ai.stolen.clear(); w->ai.state = Text::Token::Ai::State::Idle; - w->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(w->text))); + w->recolor (); + w->set_ColorSource(Text::ColorSource::Lint); continue; } @@ -161,6 +163,7 @@ void WordAiSystem::update_Attack(Text::Token::Data& w, const Player& p, CarriedL Text::TokenPtr stolen = std::move(node->word); stolen->body.mode = PhysicsMode::AiHover; stolen->set_Color(ConfigColor::attack); + stolen->set_ColorSource(Text::ColorSource::Static); w.ai.stolen.push_back(std::move(stolen)); enter_Flee(w); carried.compact(); @@ -269,7 +272,9 @@ WordAiSystem::release_StolenWords(Text::Token::Data& w, DynArray<Text::TokenPtr> if (!s) continue; s->body.mode = PhysicsMode::Dynamic; s->body.vel = { w.body.vel.x * 0.5, 2.0, w.body.vel.z * 0.5 }; - s->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(s->text))); + s->recolor (); + s->set_ColorSource(Text::ColorSource::Lint); + out.push_back(std::move(s)); } w.ai.stolen.clear(); @@ -280,6 +285,7 @@ void WordAiSystem::enter_Attack(Text::Token::Data& w) { w.set_Color(ConfigColor::attack); + w.set_ColorSource(Text::ColorSource::Static); w.body.mode = PhysicsMode::AiHover; w.ai.state = Text::Token::Ai::State::Attack; w.ai.state_timer = 0.0; @@ -297,6 +303,7 @@ void WordAiSystem::enter_Flee(Text::Token::Data& w) { w.set_Color(ConfigColor::flee); + w.set_ColorSource(Text::ColorSource::Static); w.body.mode = PhysicsMode::AiHover; w.ai.state = Text::Token::Ai::State::Flee; w.ai.state_timer = 0.0; @@ -313,6 +320,7 @@ WordAiSystem::enter_Stunned(Text::Token::Data& w) w.ai.state_timer = 0.0; w.body.vel = Vec3{}; w.set_Color(ConfigColor::stunned); + w.set_ColorSource(Text::ColorSource::Static); } @@ -323,7 +331,8 @@ WordAiSystem::enter_Idle(Text::Token::Data& w) w.ai.state_timer = 0.0; w.body.mode = PhysicsMode::Hover; w.body.hover_height = w.ai.hover_height_saved; - w.set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(w.text))); + w.recolor(); + w.set_ColorSource(Text::ColorSource::Lint); } diff --git a/src/text/handler.cpp b/src/text/handler.cpp index 8fa461d..9ee98e3 100644 --- a/src/text/handler.cpp +++ b/src/text/handler.cpp @@ -103,13 +103,13 @@ Handler::set_DefaultStyle(float depth, float scale, double line_height, double w } -void -Handler::clear() +void Handler::clear() { _lines .clear(); _last_src .clear(); _label_off .clear(); _label_text.clear(); + _view_tag = 0; mark_Dirty(); } @@ -144,8 +144,6 @@ Handler::set_Lines(const DynArray<std::string>& lines) void Handler::append_Line(const std::string& text) { - SPIKE("append_Line"); - for (Line& l : _lines) { l.y += _line_height; for (TokenPtr& t : l.tokens) if (t) t->pos.y += _line_height; @@ -168,8 +166,6 @@ Handler::append_Line(const std::string& text) void Handler::append_Tokens(DynArray<TokenPtr> block) { - SPIKE("append_Tokens"); - DynArray<TokenPtr> toks; toks.reserve(block.size()); for (TokenPtr& t : block) if (t) toks.push_back(std::move(t)); @@ -213,7 +209,6 @@ Handler::append_Tokens(DynArray<TokenPtr> block) TokenPtr Handler::detach(Token::Data* t) { - SPIKE("detach"); if (edit_Active()) return nullptr; for (size_t li = 0; li < _lines.size(); ++li) { @@ -236,7 +231,6 @@ Handler::detach(Token::Data* t) void Handler::attach(DynArray<TokenPtr> tokens, const Vec3& impact) { - SPIKE("attach"); if (tokens.empty()) return; if (_layout == LayoutMode::Mono) attach_Mono (tokens, impact); diff --git a/src/text/handler.hpp b/src/text/handler.hpp index 9001b48..7658791 100644 --- a/src/text/handler.hpp +++ b/src/text/handler.hpp @@ -114,6 +114,8 @@ public: void set_Style (SDL_FColor color, float depth, float scale); void set_Collidable(bool on); void kick (size_t word_index, const Vec3& projectile_vel, const Vec3& normal); + bool view_Is (size_t tag) const { return tag != 0 && tag == _view_tag; } + void set_View (size_t tag) { _view_tag = tag; } bool is_Empty () const { return _lines.empty() || _lines[0].tokens.empty(); } const DynArray<TokenPtr>& get_Words () const; @@ -136,6 +138,8 @@ private: DynArray<std::string> _status; + size_t _view_tag { 0 }; // identity of currently-materialised info-wall content; 0 = none + bool _lex_enabled { true }; bool _show_numbers { false }; bool _both_columns { false }; diff --git a/src/text/mesh.cpp b/src/text/mesh.cpp index fc54790..0a09299 100644 --- a/src/text/mesh.cpp +++ b/src/text/mesh.cpp @@ -359,7 +359,7 @@ bool TextMesh::build(const std::string& font_path, const std::string& text, int size, float /*depth*/, float scale) { - SPIKE("mesh.build"); + // SPIKE("mesh.build"); verts.clear(); tris.clear(); is_front.clear(); vertex_normals.clear(); FT_Face face = acquire_Face(font_path, size); diff --git a/src/text/token.cpp b/src/text/token.cpp index a0bb79b..47084d1 100644 --- a/src/text/token.cpp +++ b/src/text/token.cpp @@ -188,7 +188,7 @@ Data::get_Distance(Vec3 position) const void Data::rebuild_Mesh() { - SPIKE("rebuild_mesh"); + // SPIKE("rebuild_mesh"); loaded = mesh.build(font_path, text, 64, depth, scale); if (!loaded) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, diff --git a/src/utils/frame_profiler.hpp b/src/utils/frame_profiler.hpp index e362eb4..bade110 100644 --- a/src/utils/frame_profiler.hpp +++ b/src/utils/frame_profiler.hpp @@ -30,10 +30,12 @@ #include <cstdio> #ifdef FRAME_PROFILER_DISABLED -# define FP_ZONE(name) ((void)0) -# define FRAME_BEGIN() ((void)0) -# define FRAME_END() ((void)0) -# define FRAME_SORT(s) ((void)0) +# define FP_ZONE(name) ((void)0) +# define FRAME_BEGIN() ((void)0) +# define FRAME_END() ((void)0) +# define FRAME_SORT_PEAK ((void)0) +# define FRAME_SORT_TOTAL ((void)0) +# define FRAME_SORT_AVERAGE ((void)0) #else diff --git a/src/wall/wall.cpp b/src/wall/wall.cpp index d5373f5..e7b0696 100644 --- a/src/wall/wall.cpp +++ b/src/wall/wall.cpp @@ -110,6 +110,9 @@ namespace Wall void show_TokenInfo(::Text::Handler& info, const std::string& token_text) { + const size_t tag = std::hash<std::string>{}(token_text) | 1; + if (info.view_Is(tag)) return; + info.clear(); Text::Token::Type tt = Text::CppHighlight::classify_Word(token_text); @@ -121,6 +124,8 @@ namespace Wall for (const std::string& line : { line1, line2, line3 }) if (!line.empty()) info.append_Line(line); + + info.set_View(tag); }