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.gzAdd Connection Pult (its a strart) With Claude
src/entities/pult.hpp | 2 + src/states/game_state.cpp | 140 ++++++++++++++++++++++++++++++++++++++++- src/states/game_state.hpp | 18 ++++++ src/systems/input_system.cpp | 29 +++++++-- src/systems/input_system.hpp | 4 ++ src/systems/program_system.cpp | 7 +++ src/systems/program_system.hpp | 1 + src/systems/pult_system.cpp | 12 +++- src/systems/pult_system.hpp | 30 +++++---- 9 files changed, 223 insertions(+), 20 deletions(-) diff --git a/src/entities/pult.hpp b/src/entities/pult.hpp index 2692dea..042947a 100644 --- a/src/entities/pult.hpp +++ b/src/entities/pult.hpp @@ -18,6 +18,8 @@ enum class PultAction : uint8_t { ClearEditor, Compile, RunProgram, + ConnectProgram, + ConnectEditor, }; diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp index 5b34ec4..8ae80c7 100644 --- a/src/states/game_state.cpp +++ b/src/states/game_state.cpp @@ -199,6 +199,8 @@ GameState::pult_DispatchAction(PultAction act) case PultAction::RunProgram: on_Run(); break; + case PultAction::ConnectProgram: connect_Begin(Conn::Program); break; + case PultAction::ConnectEditor: break; } } @@ -630,11 +632,18 @@ GameState::update(double dt) case InputCommand::TextCommit: on_TextCommit(input); break; case InputCommand::TextCancel: on_TextCancel(input); break; case InputCommand::SandboxRestore:sandbox_Restore(); break; + case InputCommand::ConnectPultSubmit: conn_Submit(); break; + case InputCommand::ConnectPultExit: connect_End(); break; } } - if (input.text_mode) { + if (input.capture_typing) { + // ctrl+WASD already set movement in InputSystem; don't clear it. + if (input.text_buffer != _conn_last) { + conn_RebuildPreview(); + _conn_last = input.text_buffer; + } + } else if (input.text_mode) { input.forward = input.backward = input.left = input.right = false; - if (input.text_buffer != _typing_last) { typing_RebuildPreview(input.text_buffer); _typing_last = input.text_buffer; @@ -694,6 +703,8 @@ GameState::update(double dt) _plate_sys->update(dt, *_ctx.player, _step_plates); _editor_wall.update(dt, _ctx.camera->pos); + + if (_conn != Conn::None) _conn_blink += dt; } @@ -776,6 +787,14 @@ GameState::render(SDL_Window& window, GlRenderer& renderer) lbl.draw(renderer, *_ctx.camera, *_ctx.light); } + { FP_ZONE("render.wall_edit"); + if (_conn != Conn::None) { + if (_conn_input) _conn_input->draw(renderer, *_ctx.camera, *_ctx.light); + conn_DrawCursor(renderer); + conn_DrawElectric(renderer); + } + } + { FP_ZONE("render._carried"); DynArray<CarriedWordNode*> nodes = _carried.nodes(); for (const CarriedWordNode* node : nodes) @@ -989,7 +1008,7 @@ GameState::substep(double dt) DynArray<ProjectileContactEvent> projectile_contacts; - DynArray<TextLabel*> all_labels = _pult_sys->labels(); + DynArray<TextLabel*> all_labels = _pult_sys->get_Labels(); { FP_ZONE("subst.hint_and_labels"); _hint_sys->update(dt, *_ctx.camera); for (TextLabel* lbl : all_labels) @@ -1348,3 +1367,118 @@ GameState::carry_BeginAnim(Text3dPtr word, bool with_pitch) _anims_add.push_back(std::move(anim)); } + + +void +GameState::connect_Begin(Conn c) +{ + if (c != Conn::Program) return; // Editor connect is Step 2 + _conn = c; + _conn_pult_pos = _pult_sys->get_ConnectProgramPos(); + _frame_input.capture_typing = true; + _frame_input.text_buffer.clear(); + _conn_last.clear(); + _conn_input.reset(); +} + +void +GameState::connect_End() +{ + _conn = Conn::None; + _frame_input.capture_typing = false; + _frame_input.text_buffer.clear(); + _conn_last.clear(); + _conn_input.reset(); +} + +void +GameState::conn_Submit() +{ + const std::string line = _frame_input.text_buffer; + _frame_input.text_buffer.clear(); + _conn_last.clear(); + _conn_input.reset(); + + if (line == ":exit") { connect_End(); return; } + if (line == ":help") { + for (const char* h : { ":exit leave the connection", + "ctrl+WASD move while connected", + "<text> + Enter send a line to the program (stdin)" }) + _program_sys->echo_Input(_ctx.wall_texts, h); + return; + } + + _program_sys->feed_Input(line); + _program_sys->echo_Input(_ctx.wall_texts, line); +} + +void +GameState::conn_RebuildPreview() +{ + Wall::Transform tf = Wall::get_Transform(Wall::Role::Program, ConfigGame::world_box_scale); + Vec3 pos = tf.position; + pos.y = ProgramSystem::start_y; + + _conn_input = make_Text3d("> " + _frame_input.text_buffer, + Text3d::depth_wall, 0.003f, pos, ConfigColor::white); + _conn_input->yaw = tf.yaw; +} + +void +GameState::conn_DrawCursor(GlRenderer& gl) +{ + if (!_conn_input) return; + if (SDL_fmod(_conn_blink, 0.8) > 0.4) return; // blink: on half the cycle + + Vec3 c = _conn_input->pos; + c.z = _conn_input->pos.z - (static_cast<double>(_conn_input->half_extent) + 0.15); // just past the line (wall +X → text runs along z) + + Mat4 view = buildViewMat4(*_ctx.camera); + Mat4 model = buildModelMat4(c, _conn_input->yaw); + Mat4 mvp = Mat4::mul(gl.projection(), Mat4::mul(view, model)); + + gl.set_Wireframe(false); + gl.set_MVP(model, mvp); + gl.set_NormalMat(model); + gl.set_Light(*_ctx.light); + gl.set_Color(0.6f, 1.0f, 1.0f); + gl.set_Emissive(0.6f, 1.0f, 1.0f, 0.9f); + gl.draw_Box(0.04f, 0.22f, 0.02f); + gl.set_Emissive(0.0f, 0.0f, 0.0f, 0.0f); +} + +void +GameState::conn_DrawElectric(GlRenderer& gl) +{ + const Vec3 a = _ctx.camera->pos; + const Vec3 b = _conn_pult_pos; + + constexpr int SEG = 16; + DynArray<float> pts; + pts.reserve(SEG * 6); + Vec3 prev = a; + for (int i = 1; i <= SEG; ++i) { + const double t = static_cast<double>(i) / SEG; + Vec3 p { a.x + (b.x - a.x) * t, + a.y + (b.y - a.y) * t, + a.z + (b.z - a.z) * t }; + if (i < SEG) { // jitter the interior for the spark look + const double j = 0.18; + p.x += ((rand() % 1000) / 1000.0 - 0.5) * j; + p.y += ((rand() % 1000) / 1000.0 - 0.5) * j; + p.z += ((rand() % 1000) / 1000.0 - 0.5) * j; + } + pts.push_back((float)prev.x); pts.push_back((float)prev.y); pts.push_back((float)prev.z); + pts.push_back((float)p.x); pts.push_back((float)p.y); pts.push_back((float)p.z); + prev = p; + } + + Mat4 ident = Mat4::identity(); + Mat4 mvp = Mat4::mul(gl.projection(), buildViewMat4(*_ctx.camera)); + gl.set_MVP(ident, mvp); + gl.set_NormalMat(ident); + gl.set_Wireframe(true); // wireframe path emits u_Color directly + gl.set_Color(0.5f, 0.9f, 1.0f); + gl.draw_Lines(pts.data(), static_cast<int>(pts.size()) / 3); + gl.set_Wireframe(false); +} diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp index 8a0adf0..4a92833 100644 --- a/src/states/game_state.hpp +++ b/src/states/game_state.hpp @@ -72,6 +72,24 @@ class GameState : public IState double _accumulator { 0.0 }; double _alpha { 0.0 }; + + + // TODO START: OBJECT ORIENT ME + enum class Conn : uint8_t { None, Program, Editor }; + Conn _conn { Conn::None }; + std::string _conn_last; // last previewed buffer + Text3dPtr _conn_input; // live typed-line preview on the wall + double _conn_blink { 0.0 }; // cursor blink accumulator + Vec3 _conn_pult_pos {}; // anchor for the electric line + void connect_Begin (Conn c); + void connect_End (); + void conn_Submit (); + void conn_RebuildPreview (); + void conn_DrawCursor (GlRenderer& gl); + void conn_DrawElectric (GlRenderer& gl); + // TODO END: OBJECT ORIENT ME + + DynArray<WallLabel> _wall_labels; DynArray<Text3d*> _typing_words; diff --git a/src/systems/input_system.cpp b/src/systems/input_system.cpp index 1e0bc08..0d53365 100644 --- a/src/systems/input_system.cpp +++ b/src/systems/input_system.cpp @@ -29,6 +29,25 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input) case SDL_EVENT_KEY_DOWN: { SDL_Keycode sym = e.key.key; + if (input.capture_typing) { + const bool ctrl = (e.key.mod & SDL_KMOD_CTRL); + if (ctrl) { + switch (sym) { + case SDLK_W: input.forward = true; break; + case SDLK_A: input.left = true; break; + case SDLK_S: input.backward = true; break; + case SDLK_D: input.right = true; break; + } + } else if (sym == SDLK_RETURN) { + input.commands.push_back(InputCommand::ConnectPultSubmit); + } else if (sym == SDLK_ESCAPE) { + input.commands.push_back(InputCommand::ConnectPultExit); + } else if (sym == SDLK_BACKSPACE && !input.text_buffer.empty()) { + input.text_buffer.pop_back(); + } + break; + } + if (input.text_mode) { if (sym == SDLK_RETURN) input.commands.push_back(InputCommand::TextCommit); @@ -47,7 +66,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input) case SDLK_D: input.right = true; break; case SDLK_E: input.pick = true; break; - case SDLK_F1: game.change_State(cpputils::make_unique<GameState>()); break; + case SDLK_F1: game.change_State(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_Q: input.commands.push_back(InputCommand::Drop); break; @@ -58,7 +77,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input) case SDLK_T: input.commands.push_back(InputCommand::TextStart); break; case SDLK_LSHIFT: case SDLK_RSHIFT: input.commands.push_back(InputCommand::FreelookStart); break; - case SDLK_ESCAPE: game.change_State(cpputils::make_unique<MenuState>()); break; + case SDLK_ESCAPE: game.change_State(cpputils::make_unique<MenuState>()); break; case SDLK_PLUS: if (e.key.mod & SDL_KMOD_CTRL) input.commands.push_back(InputCommand::NumberInc); @@ -83,13 +102,15 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input) case SDLK_E: input.pick = false; break; case SDLK_LSHIFT: - case SDLK_RSHIFT: input.commands.push_back(InputCommand::FreelookEnd); break; + case SDLK_RSHIFT: + if (!input.capture_typing) input.commands.push_back(InputCommand::FreelookEnd); + break; } break; } case SDL_EVENT_TEXT_INPUT: - if (input.text_mode) + if (input.capture_typing || input.text_mode) input.text_buffer += e.text.text; break; } diff --git a/src/systems/input_system.hpp b/src/systems/input_system.hpp index 298b7e8..c819ca5 100644 --- a/src/systems/input_system.hpp +++ b/src/systems/input_system.hpp @@ -19,6 +19,8 @@ enum class InputCommand : uint8_t { NumberInc, NumberDec, TextStart, TextCommit, TextCancel, SandboxRestore, + ConnectPultSubmit, + ConnectPultExit, }; @@ -29,6 +31,8 @@ struct FrameInput { bool right = false; bool pick = false; bool shoot = false; + bool capture_typing = false; + int look_x = 0; int look_y = 0; diff --git a/src/systems/program_system.cpp b/src/systems/program_system.cpp index 8b9f8f3..5982cd2 100644 --- a/src/systems/program_system.cpp +++ b/src/systems/program_system.cpp @@ -165,6 +165,13 @@ 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 +} + + void ProgramSystem::echo_Words(TextWallList& texts, DynArray<Text3dPtr>& words, float world_scale) { diff --git a/src/systems/program_system.hpp b/src/systems/program_system.hpp index dbf5be5..34b964b 100644 --- a/src/systems/program_system.hpp +++ b/src/systems/program_system.hpp @@ -28,6 +28,7 @@ public: void run (TextWallList&, float world_scale); void update (TextWallList&, float world_scale); void feed_Input (const std::string& text); // thrown word(s) → child stdin + void echo_Input (TextWallList&, const std::string& line); // Park already-thrown (animated) words on the Program wall as one echoed // input line, scrolling existing output up — terminal style. void echo_Words (TextWallList&, DynArray<Text3dPtr>& words, float world_scale); diff --git a/src/systems/pult_system.cpp b/src/systems/pult_system.cpp index f857828..7ab1f9a 100644 --- a/src/systems/pult_system.cpp +++ b/src/systems/pult_system.cpp @@ -71,6 +71,10 @@ PultSystem::init(DynArray<GameObjectPtr>& scenery, Pult* run = spawn_pult("Run", Wall::Role::Program, lane_2); add_btn(run, "RUN", PultAction::RunProgram, 0.0f, 0.0f); + + Pult* connp = spawn_pult("Connect", Wall::Role::Program, lane_3); + add_btn(connp, "CONN", PultAction::ConnectProgram, 0.0f, 0.0f); + _connect_program = connp; } @@ -165,10 +169,16 @@ PultSystem::draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light DynArray<TextLabel*> -PultSystem::labels() +PultSystem::get_Labels() { DynArray<TextLabel*> out; out.reserve(_pults.size()); for (Pult* p : _pults) out.push_back(&p->label); return out; } + +Vec3 +PultSystem::get_ConnectProgramPos() const +{ + return _connect_program ? _connect_program->pos : Vec3{}; +} diff --git a/src/systems/pult_system.hpp b/src/systems/pult_system.hpp index 92d5079..a1a5819 100644 --- a/src/systems/pult_system.hpp +++ b/src/systems/pult_system.hpp @@ -7,30 +7,36 @@ #include "entities/pult.hpp" #include "utils/globals.hpp" -class GlRenderer; -struct Light; +class GlRenderer; class TextLabel; -struct ProjectileContactEvent; +struct Light; struct Player; +struct ProjectileContactEvent; class PultSystem { DynArray<Pult*> _pults; + Pult* _connect_program { nullptr }; public: - void init(DynArray<GameObjectPtr>& scenery, - DynArray<PultButton*>& buttons_out, - float world_scale); + void init (DynArray<GameObjectPtr>& scenery, + DynArray<PultButton*>& buttons_out, + float world_scale); std::optional<PultAction> - try_Activate(GameObject* obj, - const DynArray<PultButton*>& buttons); + try_Activate (GameObject* obj, + const DynArray<PultButton*>& buttons); std::optional<PultAction> handle_ProjectileContact(const ProjectileContactEvent& ev, const DynArray<PultButton*>& buttons); std::optional<PultAction> - handle_PlayerContact(const Player& player, - const DynArray<PultButton*>& buttons); - void update(double dt, const Camera& camera); + handle_PlayerContact (const Player& player, + const DynArray<PultButton*>& buttons); + Vec3 get_ConnectProgramPos () const; + + DynArray<TextLabel*> + get_Labels (); + + void update (double dt, const Camera& camera); void draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light) const; - DynArray<TextLabel*> labels(); + };