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 Typing AND inc/dec numbers With Claude
src/assets/sandbox.c.last_ok | 3 +- src/core/game.cpp | 2 + src/states/game_state.cpp | 116 ++++++++++++++++++++++++++++++++++++++++--- src/states/game_state.hpp | 7 +++ src/systems/input_system.cpp | 25 ++++++++++ src/systems/input_system.hpp | 12 ++++- 6 files changed, 154 insertions(+), 11 deletions(-) diff --git a/src/assets/sandbox.c.last_ok b/src/assets/sandbox.c.last_ok index a6049fc..37f4703 100644 --- a/src/assets/sandbox.c.last_ok +++ b/src/assets/sandbox.c.last_ok @@ -21,7 +21,8 @@ int game_build ( ModObject * out , int max ) { ( void ) max ; o = out ; n = 0 ; box ( 0 , - 16 , 5 , 1 , 1 , 1 ) ; +box(2,-15,4,2,2,2); text ( 0 , - 15 , 5 , "hi" ) ; -box ( 3 , - 16 , 5 , 1 , 2 , 1 ) ; +box ( 6 , - 15 , 10 , 1 , 1 , 5 ) ; return n ; } diff --git a/src/core/game.cpp b/src/core/game.cpp index 3d25d00..af565aa 100644 --- a/src/core/game.cpp +++ b/src/core/game.cpp @@ -35,6 +35,8 @@ Game::init() if (!gl_renderer->init(window)) return false; + SDL_StartTextInput(window); + changeState(cpputils::make_unique<MenuState>()); return true; } diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp index 0ec8b41..8a500ef 100644 --- a/src/states/game_state.cpp +++ b/src/states/game_state.cpp @@ -362,6 +362,27 @@ GameState::on_FreelookEnd() ctx.player->yaw, ctx.player->pitch); } +void +GameState::on_NumberAdjust(int delta) +{ + if (carried.empty() || !carried.last || !carried.last->word) return; + Text3d* w = carried.last->word.get(); + + // Parse the text as an integer; bail if it isn't one. + const std::string& s = w->text; + if (s.empty()) return; + size_t start = (s[0] == '-') ? 1 : 0; + if (start == s.size()) return; + for (size_t i = start; i < s.size(); ++i) + if (!std::isdigit(static_cast<unsigned char>(s[i]))) return; // not a plain integer + + long val = std::strtol(s.c_str(), nullptr, 10); + val += delta; + + w->text = std::to_string(val); + w->rebuild_Mesh(); // regenerate glyphs + half_extent for the new text +} + void GameState::step_Spawn(const std::vector<std::string>& tokens) @@ -478,14 +499,42 @@ GameState::update(double dt) { FP_ZONE("update.input_dispatch"); for (InputCommand cmd : input.commands) { switch (cmd) { - case InputCommand::Jump: on_Jump(); break; - case InputCommand::Click: on_Click(); break; - case InputCommand::Drop: on_DropLast(); break; - case InputCommand::ShootAll: on_ShootAll(); break; - case InputCommand::Compile: on_Compile(); break; - case InputCommand::DebugPrint: on_DebugPrint(); break; - case InputCommand::FreelookStart: on_FreelookStart(); break; - case InputCommand::FreelookEnd: on_FreelookEnd(); break; + case InputCommand::Jump: on_Jump(); break; + case InputCommand::Click: on_Click(); break; + case InputCommand::Drop: on_DropLast(); break; + case InputCommand::ShootAll: on_ShootAll(); break; + case InputCommand::Compile: on_Compile(); break; + case InputCommand::DebugPrint: on_DebugPrint(); break; + case InputCommand::FreelookStart: on_FreelookStart(); break; + case InputCommand::FreelookEnd: on_FreelookEnd(); break; + case InputCommand::NumberInc: on_NumberAdjust(+1); break; + case InputCommand::NumberDec: on_NumberAdjust(-1); break; + case InputCommand::TextStart: + input.text_mode = true; + input.text_buffer.clear(); + typing_words.clear(); + typing_last.clear(); + break; + case InputCommand::TextCommit: + typing_words.clear(); + typing_last.clear(); + input.text_mode = false; + input.text_buffer.clear(); + break; + case InputCommand::TextCancel: + typing_ClearPreview(); + typing_last.clear(); + input.text_mode = false; + input.text_buffer.clear(); + break; + } + } + 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; } } if (pick_cooldown > 0.0) pick_cooldown -= dt; @@ -1209,7 +1258,58 @@ GameState::sandbox_LoadFileToWall() } } + void GameState::sandbox_Restore() { 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) +{ + // Remove the previous preview words from the carry line. + typing_ClearPreview(); + + auto [anchor, right, glyph_yaw] = anchorframe_Current(); + std::istringstream in(s); + std::string tok; + while (in >> tok) { + 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; + typing_words.push_back(t.get()); // track raw ptr for later removal + carried.push(std::move(t)); + } +} + + +void +GameState::typing_ClearPreview() +{ + // Preview words are the most recently pushed → at the tail. Pop exactly + // as many as we added; let them be destroyed (don't re-add to scene). + for (size_t i = 0; i < typing_words.size(); ++i) { + if (carried.empty()) break; + carried.pop_back(); // returns Text3dPtr; dropping it frees the word + } + typing_words.clear(); +} diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp index 639837a..e954df0 100644 --- a/src/states/game_state.hpp +++ b/src/states/game_state.hpp @@ -81,6 +81,9 @@ class GameState : public IState double accumulator { 0.0 }; double alpha { 0.0 }; + std::vector<Text3d*> typing_words; + std::string typing_last; + std::vector<Projectile> projectiles; std::vector<WordAnim_Add> anims_add; std::vector<WordAnim_Drop> anims_drop; @@ -115,6 +118,7 @@ class GameState : public IState void on_FreelookStart(); void on_FreelookEnd(); void on_Compile(); + void on_NumberAdjust(int dt); void scene_Setup(const Scene& scene); void scene_Teardown(); @@ -135,6 +139,9 @@ class GameState : public IState void sandbox_Restore(); // copy backup → canonical, reload wall 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(); bool level_TryAdvance(); void level_Advance(); diff --git a/src/systems/input_system.cpp b/src/systems/input_system.cpp index bd5ccf1..282d78f 100644 --- a/src/systems/input_system.cpp +++ b/src/systems/input_system.cpp @@ -29,6 +29,17 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input) case SDL_EVENT_KEY_DOWN: { SDL_Keycode sym = e.key.key; + if (input.text_mode) { + if (sym == SDLK_RETURN) + input.commands.push_back(InputCommand::TextCommit); + else if (sym == SDLK_ESCAPE) + input.commands.push_back(InputCommand::TextCancel); + else if (sym == SDLK_BACKSPACE && !input.text_buffer.empty()) + input.text_buffer.pop_back(); + break; // swallow everything else — no movement while typing + } + + switch (sym) { case SDLK_W: input.forward = true; break; case SDLK_A: input.left = true; break; @@ -42,9 +53,18 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input) case SDLK_F: input.commands.push_back(InputCommand::ShootAll); break; case SDLK_C: input.commands.push_back(InputCommand::Compile); break; case SDLK_P: input.commands.push_back(InputCommand::DebugPrint); break; + 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.changeState(cpputils::make_unique<MenuState>()); break; + case SDLK_PLUS: + if (e.key.mod & SDL_KMOD_CTRL) + input.commands.push_back(InputCommand::NumberInc); + break; + case SDLK_MINUS: + if (e.key.mod & SDL_KMOD_CTRL) + input.commands.push_back(InputCommand::NumberDec); + break; } break; @@ -65,5 +85,10 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input) } break; } + + case SDL_EVENT_TEXT_INPUT: + if (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 c93d7d4..85bba4b 100644 --- a/src/systems/input_system.hpp +++ b/src/systems/input_system.hpp @@ -1,5 +1,7 @@ #pragma once + #include <SDL3/SDL.h> +#include <string> #include <vector> class Game; @@ -11,8 +13,9 @@ enum class InputCommand : uint8_t { ShootAll, Compile, DebugPrint, - FreelookStart, - FreelookEnd + FreelookStart, FreelookEnd, + NumberInc, NumberDec, + TextStart, TextCommit, TextCancel }; @@ -27,6 +30,11 @@ struct FrameInput { int look_x = 0; int look_y = 0; + bool text_mode = false; // currently typing + std::string text_buffer; // accumulated text + bool text_commit = false; // Enter pressed this frame + bool text_cancel = false; // Esc pressed this frame + std::vector<InputCommand> commands; };