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.gzReorder member vars
assets/sandbox.c | 4 +- assets/sandbox.c.last_ok | 4 +- src/core/game.hpp | 2 +- src/entities/camera.hpp | 11 ++- src/entities/game_object.hpp | 24 ++--- src/entities/plate.hpp | 8 +- src/entities/player.cpp | 8 +- src/entities/player.hpp | 13 +-- src/entities/pult.hpp | 14 +-- src/entities/world.hpp | 4 +- src/levels/director.hpp | 12 ++- src/levels/level.hpp | 23 ++--- src/levels/level/char_copy.cpp | 146 ++++++++++++++-------------- src/levels/level/sandbox.cpp | 14 +-- src/levels/level/tutorial.cpp | 182 +++++++++++++++++------------------ src/levels/scene.hpp | 53 +++++----- src/rendering/floor_surface.hpp | 8 +- src/rendering/gl_renderer.hpp | 28 +++--- src/states/game_context.hpp | 8 +- src/states/game_state.cpp | 16 +-- src/states/game_state.hpp | 54 +++++------ src/states/menu_state.hpp | 16 +-- src/systems/aim.hpp | 4 +- src/systems/celebration_system.hpp | 4 +- src/systems/collision.hpp | 18 ++-- src/systems/compiler_system.hpp | 8 +- src/systems/cpp_highlight_system.hpp | 14 +-- src/systems/dyn_loader_system.hpp | 9 +- src/systems/freelook_system.hpp | 2 +- src/systems/hint_system.hpp | 2 +- src/systems/input_system.hpp | 32 +++--- src/systems/particle_system.hpp | 12 +-- src/systems/physics_system.cpp | 14 ++- src/systems/physics_system.hpp | 9 +- src/systems/pick_system.hpp | 9 +- src/systems/program_system.hpp | 10 +- src/systems/projectile_system.hpp | 56 ++++++----- src/systems/pult_system.hpp | 4 +- src/systems/wall_connection.hpp | 15 +-- src/systems/word_anim_system.hpp | 11 ++- src/text/handler.hpp | 11 ++- src/text/mesh.hpp | 2 +- src/text/text.hpp | 11 ++- src/text/vim_buffer.hpp | 36 +++---- src/utils/config.hpp | 3 +- src/utils/light.hpp | 15 ++- src/wall/label.hpp | 6 +- src/wall/wall.hpp | 7 +- 48 files changed, 500 insertions(+), 476 deletions(-) diff --git a/assets/sandbox.c b/assets/sandbox.c index acc21b5..ac2a96b 100644 --- a/assets/sandbox.c +++ b/assets/sandbox.c @@ -25,7 +25,7 @@ int game_build ( DynObject * out , int max ) { o = out ; n = 0 ; text ( 0 , - 15 , 5 , "hi" ) ; box ( 0 , - 16 , 5 , 2 , 1 , 2 , 0 ) ; -for ( int i = 0 ; i < 10 ; i ++ ) -box ( 2 * i , - 16 , 5 , 2 , 0 + i , 2 , 0 ) ; +for ( int i = 0 ; i < 100 ; i ++ ) +box ( 0 + i , - 16 , 5 + i % 4 , 1 , 1 + i * 0.5 , 1 , 0 ) ; return n ; } diff --git a/assets/sandbox.c.last_ok b/assets/sandbox.c.last_ok index acc21b5..ac2a96b 100644 --- a/assets/sandbox.c.last_ok +++ b/assets/sandbox.c.last_ok @@ -25,7 +25,7 @@ int game_build ( DynObject * out , int max ) { o = out ; n = 0 ; text ( 0 , - 15 , 5 , "hi" ) ; box ( 0 , - 16 , 5 , 2 , 1 , 2 , 0 ) ; -for ( int i = 0 ; i < 10 ; i ++ ) -box ( 2 * i , - 16 , 5 , 2 , 0 + i , 2 , 0 ) ; +for ( int i = 0 ; i < 100 ; i ++ ) +box ( 0 + i , - 16 , 5 + i % 4 , 1 , 1 + i * 0.5 , 1 , 0 ) ; return n ; } diff --git a/src/core/game.hpp b/src/core/game.hpp index 949de2b..9112de0 100644 --- a/src/core/game.hpp +++ b/src/core/game.hpp @@ -11,10 +11,10 @@ class Game { - SDL_Window* _window {}; GlRendererPtr _gl_renderer{}; cpputils::unique_ptr<IState> _state; + SDL_Window* _window {}; public: ~Game(); diff --git a/src/entities/camera.hpp b/src/entities/camera.hpp index 94be3be..ac1030b 100644 --- a/src/entities/camera.hpp +++ b/src/entities/camera.hpp @@ -8,15 +8,16 @@ struct Camera { - int32_t pitch {}; - uint32_t yaw {}; - double yaw_r {}; - double pitch_r{}; - Vec3 pos {}; Vec3 fwd {}; Vec3 right{}; + double yaw_r {}; + double pitch_r{}; + + int32_t pitch {}; + uint32_t yaw {}; + void sync_FromPlayer(const Player& player) { pos = player.prev_pos; diff --git a/src/entities/game_object.hpp b/src/entities/game_object.hpp index e3a1888..53c6da8 100644 --- a/src/entities/game_object.hpp +++ b/src/entities/game_object.hpp @@ -35,14 +35,14 @@ struct CollisionShape struct RigidBody { - bool hover_disturbed{false}; + Vec3 vel {}; + Vec3 force{}; double friction {0.8}; double hover_height{0.0}; double hover_phase {0.0}; double mass {1.0}; double restitution {0.3}; - Vec3 vel {}; - Vec3 force{}; + bool hover_disturbed{false}; PhysicsMode mode {PhysicsMode::Hover}; @@ -88,23 +88,23 @@ struct RigidBody struct GameObject { - bool carriable {true}; - bool interactable {true}; - bool solid {true}; + CollisionShape shape; + RigidBody body; + ObjectType type{ObjectType::Text}; - float yaw {0.0f}; Vec3 pos {}; - ObjectType type{ObjectType::Text}; - CollisionShape shape; - RigidBody body; + float yaw {0.0f}; + + bool carriable {true}; + bool interactable {true}; + bool solid {true}; GameObject() {} GameObject(Vec3 p, ObjectType t, CollisionShape sh, RigidBody b, bool c = true, bool i = true, bool s = true) - : carriable(c), interactable(i), solid(s), - pos(p), type(t), shape(sh), body(b) {} + : shape(sh), body(b), type(t), pos(p), carriable(c), interactable(i), solid(s) {} virtual ~GameObject() = default; diff --git a/src/entities/plate.hpp b/src/entities/plate.hpp index 47834d2..69b3b2e 100644 --- a/src/entities/plate.hpp +++ b/src/entities/plate.hpp @@ -15,11 +15,13 @@ struct Plate final : public SolidBox static constexpr float _HALF_H = 0.05f; static constexpr float _HALF_D = 0.6f; - bool active { false }; - bool was_active{ false }; + Vec3 base_pos {}; + double bob_phase { 0.0 }; double pulse { 0.0 }; - Vec3 base_pos {}; + + bool active { false }; + bool was_active{ false }; Plate(Vec3 world_pos) : SolidBox( diff --git a/src/entities/player.cpp b/src/entities/player.cpp index e8f70a0..a50acca 100644 --- a/src/entities/player.cpp +++ b/src/entities/player.cpp @@ -3,12 +3,12 @@ Player::Player() - : pitch (-0x08000000), - yaw (0x20000000), + : pos {8.0, 0.0, 8.0}, + vel {0.0, 0.0, 0.0}, radius(0.5f), height(1.5f), - pos {8.0, 0.0, 8.0}, - vel {0.0, 0.0, 0.0} + pitch (-0x08000000), + yaw (0x20000000) {} void diff --git a/src/entities/player.hpp b/src/entities/player.hpp index 02baa0b..1c4fec1 100644 --- a/src/entities/player.hpp +++ b/src/entities/player.hpp @@ -23,16 +23,17 @@ struct Player public: Player(); - bool on_ground{false}; - int32_t pitch {}; - uint32_t yaw {}; - float radius {0.5f}; - float height {1.5f}; - Vec3 pos {}; Vec3 prev_pos {}; Vec3 vel {}; + float radius {0.5f}; + float height {1.5f}; + + int32_t pitch {}; + uint32_t yaw {}; + bool on_ground{false}; + void compute_Movement(const FrameInput& input); void add_Look (int dx, int dy); void apply_Collision (const PlayerCollision& col); diff --git a/src/entities/pult.hpp b/src/entities/pult.hpp index 53c93fc..63fc6a8 100644 --- a/src/entities/pult.hpp +++ b/src/entities/pult.hpp @@ -25,11 +25,13 @@ enum class PultAction : uint8_t { struct PultButton final : public SolidBox { std::string label; + PultAction action; + double press_timer { 0.0 }; + bool pressed { false }; bool player_on { false }; - double press_timer { 0.0 }; PultButton(const std::string& lbl, PultAction act, Vec3 world_pos); @@ -41,15 +43,15 @@ struct PultButton final : public SolidBox struct Pult : public SolidBox { - std::string name; - Text::Handler label; - DynArray<PultButton*> - buttons; - static constexpr float W = 0.50f; static constexpr float H = 0.28f; static constexpr float D = 0.40f; + Text::Handler label; + DynArray<PultButton*> + buttons; + std::string name; + float half_w { W }; float half_d { D }; diff --git a/src/entities/world.hpp b/src/entities/world.hpp index 40b2181..8d27f6b 100644 --- a/src/entities/world.hpp +++ b/src/entities/world.hpp @@ -11,11 +11,11 @@ struct SweepResult { - Vec3 pos; - Vec3 vel; bool hit_x{}; bool hit_y{}; bool hit_z{}; + Vec3 pos; + Vec3 vel; }; diff --git a/src/levels/director.hpp b/src/levels/director.hpp index 5ae42ee..3cc7c43 100644 --- a/src/levels/director.hpp +++ b/src/levels/director.hpp @@ -51,14 +51,16 @@ struct StepPlate class Director { - cpputils::unique_ptr<Level::Progression> - _runner; - double _step_age { 0.0 }; - - StepLedger _ledger; DynArray<StepPlate> _step_plates; DynArray<GameObject*> _step_scenery; + StepLedger _ledger; + + cpputils::unique_ptr<Level::Progression> + _runner; + + double _step_age { 0.0 }; + mutable const Level::Step* _detail_built_for { nullptr }; mutable DynArray<Text::TokenPtr> _detail_tokens; diff --git a/src/levels/level.hpp b/src/levels/level.hpp index 0adf036..c0cb580 100644 --- a/src/levels/level.hpp +++ b/src/levels/level.hpp @@ -30,28 +30,29 @@ namespace Level struct Step { - bool reset_carry = false; - int max_ai_words = 5; - - Scene::Data scene; + DynArray<TokenGrant> + grants{}; + Scene::Data scene{}; Goal::Type goal = Goal::AllOf{}; - std::string hint; - std::string details; + std::string hint{}; + std::string details{}; + + bool reset_carry = false; + int max_ai_words = 5; - DynArray<TokenGrant> grants; }; struct Data { - bool dynamic_lib = false; + DynArray<Step> steps {}; - std::string name; - std::string desc; + std::string name{}; + std::string desc{}; std::string source_file{}; - DynArray<Step> steps; + bool dynamic_lib = false; }; extern Data build_CharCopy(); diff --git a/src/levels/level/char_copy.cpp b/src/levels/level/char_copy.cpp index 4e579de..d6ba282 100644 --- a/src/levels/level/char_copy.cpp +++ b/src/levels/level/char_copy.cpp @@ -6,14 +6,21 @@ Level::Data Level::build_CharCopy() { return { - .dynamic_lib = false, - .name = "Char Copy", - .desc = "Implement a function that copies a char array and prints the result.", .steps = { // ── Step 0: main scaffold ───────────────────────────────────── { - .reset_carry = false, - .max_ai_words = 3, + .grants = { + Level::TokenGrant::give("int"), + Level::TokenGrant::give("main"), + Level::TokenGrant::give("("), + Level::TokenGrant::choice({ "void", "int", "char" }), + Level::TokenGrant::give(")"), + Level::TokenGrant::give("{"), + Level::TokenGrant::give("return"), + Level::TokenGrant::give("0"), + Level::TokenGrant::give(";"), + Level::TokenGrant::give("}"), + }, .scene = {}, .goal = Goal::CompilePattern{ "int main ( void ) { return 0 ; }" }, .hint = "Build a working main()", @@ -28,23 +35,28 @@ Goal: } Grab tokens and arrange them on the editor wall.)", + .reset_carry = false, + .max_ai_words = 3, + }, + // ── Step 1: copy_str signature ──────────────────────────────── + { .grants = { - Level::TokenGrant::give("int"), - Level::TokenGrant::give("main"), + Level::TokenGrant::give("void"), + Level::TokenGrant::give("copy_str"), Level::TokenGrant::give("("), - Level::TokenGrant::choice({ "void", "int", "char" }), + Level::TokenGrant::choice({ "char", "int", "float" }), + Level::TokenGrant::give("src"), + Level::TokenGrant::give("["), + Level::TokenGrant::give("]"), + Level::TokenGrant::give(","), + Level::TokenGrant::give("char"), + Level::TokenGrant::give("dst"), + Level::TokenGrant::give("["), + Level::TokenGrant::give("]"), Level::TokenGrant::give(")"), Level::TokenGrant::give("{"), - Level::TokenGrant::give("return"), - Level::TokenGrant::give("0"), - Level::TokenGrant::give(";"), Level::TokenGrant::give("}"), }, - }, - // ── Step 1: copy_str signature ──────────────────────────────── - { - .reset_carry = false, - .max_ai_words = 3, .scene = {}, .goal = Goal::CompilePattern{ "void copy_str ( char src [ ] , char dst [ ] ) { }" }, .hint = "Declare the copy_str function", @@ -62,28 +74,16 @@ Goal: int main(void) { ... } Place this BEFORE main.)", - .grants = { - Level::TokenGrant::give("void"), - Level::TokenGrant::give("copy_str"), - Level::TokenGrant::give("("), - Level::TokenGrant::choice({ "char", "int", "float" }), - Level::TokenGrant::give("src"), - Level::TokenGrant::give("["), - Level::TokenGrant::give("]"), - Level::TokenGrant::give(","), - Level::TokenGrant::give("char"), - Level::TokenGrant::give("dst"), - Level::TokenGrant::give("["), - Level::TokenGrant::give("]"), - Level::TokenGrant::give(")"), - Level::TokenGrant::give("{"), - Level::TokenGrant::give("}"), - }, + .reset_carry = false, + .max_ai_words = 3, }, // ── Step 2: loop counter ────────────────────────────────────── { - .reset_carry = false, - .max_ai_words = 3, + .grants = { + Level::TokenGrant::choice({ "int", "float", "char" }), + Level::TokenGrant::give("i"), + Level::TokenGrant::give(";"), + }, .scene = {}, .goal = Goal::CompilePattern{ "void copy_str ( char src [ ] , char dst [ ] ) { int i ; }" }, .hint = "Add a loop counter inside copy_str", @@ -95,16 +95,19 @@ Goal (inside copy_str): You only need to add ONE line. Place it between the { and } of copy_str.)", - .grants = { - Level::TokenGrant::choice({ "int", "float", "char" }), - Level::TokenGrant::give("i"), - Level::TokenGrant::give(";"), - }, + .reset_carry = false, + .max_ai_words = 3, }, // ── Step 3: for loop scaffold ───────────────────────────────── { - .reset_carry = false, - .max_ai_words = 3, + .grants = { + Level::TokenGrant::give("for"), Level::TokenGrant::give("("), + Level::TokenGrant::give("i"), Level::TokenGrant::give("="), Level::TokenGrant::give("0"), Level::TokenGrant::give(";"), + Level::TokenGrant::give("i"), Level::TokenGrant::give("<"), Level::TokenGrant::give("strlen"), + Level::TokenGrant::give("("), Level::TokenGrant::give("src"), Level::TokenGrant::give(")"), Level::TokenGrant::give(";"), + Level::TokenGrant::give("i"), Level::TokenGrant::give("++"), Level::TokenGrant::give(")"), + Level::TokenGrant::give("{"), Level::TokenGrant::give("}"), + }, .scene = {}, .goal = Goal::CompilePattern{ "for ( i = 0 ; i < strlen ( src ) ; i ++ ) { }" }, .hint = "Loop through src", @@ -118,19 +121,17 @@ Goal: } This is the loop scaffold only — the body comes next.)", - .grants = { - Level::TokenGrant::give("for"), Level::TokenGrant::give("("), - Level::TokenGrant::give("i"), Level::TokenGrant::give("="), Level::TokenGrant::give("0"), Level::TokenGrant::give(";"), - Level::TokenGrant::give("i"), Level::TokenGrant::give("<"), Level::TokenGrant::give("strlen"), - Level::TokenGrant::give("("), Level::TokenGrant::give("src"), Level::TokenGrant::give(")"), Level::TokenGrant::give(";"), - Level::TokenGrant::give("i"), Level::TokenGrant::give("++"), Level::TokenGrant::give(")"), - Level::TokenGrant::give("{"), Level::TokenGrant::give("}"), - }, + .reset_carry = false, + .max_ai_words = 3, }, // ── Step 4: copy the character ──────────────────────────────── { - .reset_carry = false, - .max_ai_words = 3, + .grants = { + Level::TokenGrant::give("dst"), Level::TokenGrant::give("["), Level::TokenGrant::give("i"), Level::TokenGrant::give("]"), + Level::TokenGrant::give("="), + Level::TokenGrant::give("src"), Level::TokenGrant::give("["), Level::TokenGrant::give("i"), Level::TokenGrant::give("]"), + Level::TokenGrant::give(";"), + }, .scene = {}, .goal = Goal::CompilePattern{ "dst [ i ] = src [ i ] ;" }, .hint = "Copy one character per iteration", @@ -141,30 +142,11 @@ copies the i-th character on each pass through the loop. Goal (inside the for loop): dst[i] = src[i];)", - .grants = { - Level::TokenGrant::give("dst"), Level::TokenGrant::give("["), Level::TokenGrant::give("i"), Level::TokenGrant::give("]"), - Level::TokenGrant::give("="), - Level::TokenGrant::give("src"), Level::TokenGrant::give("["), Level::TokenGrant::give("i"), Level::TokenGrant::give("]"), - Level::TokenGrant::give(";"), - }, + .reset_carry = false, + .max_ai_words = 3, }, // ── Step 5: call copy_str and print ────────────────────────── { - .reset_carry = false, - .max_ai_words = 3, - .scene = {}, - .goal = Goal::CompilePattern{ "char dst [ 100 ] ; copy_str ( \"hello\" , dst ) ; printf ( \"%s\" , dst ) ;" }, - .hint = "Call copy_str and print the result", - .details = R"(In main(), do three things before 'return 0;': - -1. Declare a buffer: char dst[100]; -2. Call copy_str: copy_str("hello", dst); -3. Print the result: printf("%s", dst); - -The buffer holds the copied characters. -copy_str fills it. printf displays it. - -Place all three lines BEFORE the existing return.)", .grants = { Level::TokenGrant::give("char"), Level::TokenGrant::give("dst"), Level::TokenGrant::give("["), Level::TokenGrant::give("100"), Level::TokenGrant::give("]"), Level::TokenGrant::give(";"), @@ -179,7 +161,25 @@ Place all three lines BEFORE the existing return.)", Level::TokenGrant::give(","), Level::TokenGrant::give("dst"), Level::TokenGrant::give(")"), Level::TokenGrant::give(";"), }, + .scene = {}, + .goal = Goal::CompilePattern{ "char dst [ 100 ] ; copy_str ( \"hello\" , dst ) ; printf ( \"%s\" , dst ) ;" }, + .hint = "Call copy_str and print the result", + .details = R"(In main(), do three things before 'return 0;': + +1. Declare a buffer: char dst[100]; +2. Call copy_str: copy_str("hello", dst); +3. Print the result: printf("%s", dst); + +The buffer holds the copied characters. +copy_str fills it. printf displays it. + +Place all three lines BEFORE the existing return.)", + .reset_carry = false, + .max_ai_words = 3, }, }, + .name = "Char Copy", + .desc = "Implement a function that copies a char array and prints the result.", + .dynamic_lib = false, }; } diff --git a/src/levels/level/sandbox.cpp b/src/levels/level/sandbox.cpp index fdcd169..821c922 100644 --- a/src/levels/level/sandbox.cpp +++ b/src/levels/level/sandbox.cpp @@ -6,14 +6,9 @@ Level::Data Level::build_Sandbox() { return { - .dynamic_lib = true, - .name = "Sandbox", - .desc = "Build the world yourself: compile a library that spawns objects.", - .source_file = "assets/sandbox.c", .steps = { { - .reset_carry = true, - .max_ai_words = 0, + .grants = { }, .scene = {}, .goal = Goal::AllOf{}, .hint = "Spawn a box with box(x,y,z, hx,hy,hz);", @@ -30,8 +25,13 @@ Build something like: box(0, 5, -3, 1, 1, 1, 0); Then press the Compile pult. Edit and recompile freely.)", - .grants = { }, + .reset_carry = true, + .max_ai_words = 0, }, }, + .name = "Sandbox", + .desc = "Build the world yourself: compile a library that spawns objects.", + .source_file = "assets/sandbox.c", + .dynamic_lib = true, }; } diff --git a/src/levels/level/tutorial.cpp b/src/levels/level/tutorial.cpp index 33394eb..af4bab0 100644 --- a/src/levels/level/tutorial.cpp +++ b/src/levels/level/tutorial.cpp @@ -7,17 +7,14 @@ Level::Data Level::build_Tutorial() { return { - .name = "Tutorial", - .desc = "Learn the controls and the code-building loop.", .steps = { { - .reset_carry = true, - .max_ai_words = 0, + .grants = {}, .scene = Scene::Data{ .ai_seeds = {}, .plates = { - { .pos = { 8.6, World::get_FloorY<double>() + 2.2, 0.0 }, .tag = "" }, + { .tag = "", .pos = { 8.6, World::get_FloorY<double>() + 2.2, 0.0 } }, }, .platforms = { { .pos = { 5.0, World::get_FloorY<double>() + 0.4, 0.0 }, .half_extents = { 0.9, 0.2, 0.9 } }, @@ -36,16 +33,16 @@ Move the mouse to turn. Jump with SPACE. Jump on the plate on top of the platform. )", - .grants = {}, + .reset_carry = true, + .max_ai_words = 0, }, { - .reset_carry = true, - .max_ai_words = 0, + .grants = {}, .scene = Scene::Data{ .ai_seeds = {}, .plates = { - { .pos = { 0.0, World::get_FloorY<double>() + 1.2, 6.0 }, .tag = "" }, + { .tag = "", .pos = { 0.0, World::get_FloorY<double>() + 1.2, 6.0 } }, }, .platforms = { { .pos = { 0.0, World::get_FloorY<double>() + 0.6, 6.0 }, .half_extents = { 1.0, 0.4, 1.0 } }, @@ -62,18 +59,18 @@ Four walls surround you: - Program: shows your program's output - Info: this wall, with hints )", - .grants = {}, + .reset_carry = true, + .max_ai_words = 0, }, { - .reset_carry = true, - .max_ai_words = 0, + .grants = {}, .scene = Scene::Data{ .ai_seeds = {}, .plates = {}, .platforms = {}, .words = { - { .pos = { 5.0, World::get_FloorY<double>() + 1.5, 0.0 }, .text = "hello" }, + { .text = "hello", .pos = { 5.0, World::get_FloorY<double>() + 1.5, 0.0 } }, }, }, .goal = Goal::CollectWord{ "hello" }, @@ -85,20 +82,20 @@ Picked words trail in front of you as you move. > hold SHIFT to free-look without turning your body.") )", - .grants = {}, + .reset_carry = true, + .max_ai_words = 0, }, { - .reset_carry = false, - .max_ai_words = 0, + .grants = {}, .scene = Scene::Data{ .ai_seeds = {}, .plates = {}, .platforms = {}, .words = { - { .pos = { 5.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "keep", }, - { .pos = { 0.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "e", }, - { .pos = { -5.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "pressed", }, + { .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 } }, }, }, .goal = Goal::AllOf{{ @@ -111,12 +108,12 @@ Picked words trail in front of you as you move. Collect "press", "any" and "key" with 'e'. They queue up in the order you grab them.)", - .grants = {}, + .reset_carry = false, + .max_ai_words = 0, }, { - .reset_carry = false, - .max_ai_words = 0, + .grants = {}, .scene = {}, .goal = Goal::DroppedWord{}, .hint = "Press 'q' to drop a word", @@ -124,18 +121,18 @@ They queue up in the order you grab them.)", The dropped word falls to the floor — handy for putting back something you grabbed by mistake.)", - .grants = {}, + .reset_carry = false, + .max_ai_words = 0, }, { - .reset_carry = false, - .max_ai_words = 0, + .grants = {}, .scene = { .words = { - { .pos = { 5.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "keep" }, - { .pos = { 3.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "left" }, - { .pos = { 0.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "click" }, - { .pos = { 2.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "pressed" }, + { .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 }, }, }, }, .goal = Goal::ShotWord{}, @@ -145,45 +142,45 @@ putting back something you grabbed by mistake.)", Aim with the crosshair and throw — any direction is fine. Carrying several? Tap F to throw the WHOLE line at once.)", - .grants = {}, - }, - { .reset_carry = false, .max_ai_words = 0, + }, + { + .grants = {}, .scene = { .words = { - { .pos = { 1.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "f", }, - { .pos = { 2.0, World::get_FloorY<double>() + 1.5, -2.0 }, .text = "for", }, - { .pos = { 0.0, World::get_FloorY<double>() + 1.5, -3.0 }, .text = "flush",}, + { .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 }, }, }, }, .goal = Goal::ShotWord{}, .hint = "Press 'f' to flush all carried words", .details = R"(Bla blub ...)", - .grants = {}, + .reset_carry = false, + .max_ai_words = 0, }, { - .reset_carry = true, - .max_ai_words = 0, + .grants = {}, .scene = Scene::Data{ .ai_seeds = { { - .aggressiveness = 1.0, - .pos = { -6.0, World::get_FloorY<double>() + 2.0, -6.0 }, .text = "danger", + .pos = { -6.0, World::get_FloorY<double>() + 2.0, -6.0 }, .initial_state = Text::Token::Ai::State::Attack, + .aggressiveness = 1.0, }, }, .plates = {}, .platforms = {}, .words = { - { .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 }, .text = "ammo", }, + { .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 }, }, }, }, .goal = Goal::AllOf{{ Goal::CollectWord{ "ammo" }, Goal::AiWordDefeated{ "danger" } }}, @@ -194,12 +191,23 @@ you carry. Pick up "ammo", then knock out the red "danger" word by throwing at it with LEFT CLICK before it reaches you.)", - .grants = {}, + .reset_carry = true, + .max_ai_words = 0, }, { - .reset_carry = true, - .max_ai_words = 0, + .grants = { + Level::TokenGrant::give("int"), + Level::TokenGrant::give("main"), + Level::TokenGrant::give("("), + Level::TokenGrant::give("void"), + Level::TokenGrant::give(")"), + Level::TokenGrant::give("{"), + Level::TokenGrant::give("return"), + Level::TokenGrant::give("0"), + Level::TokenGrant::give(";"), + Level::TokenGrant::give("}"), + }, .scene = {}, .goal = Goal::CompilePattern{ "int main ( void ) { return 0 ; }" }, .hint = "Build int main(void) { return 0; } and press BUILD", @@ -219,23 +227,20 @@ Build exactly: Then walk to the Compile pult (labelled BUILD) and press it (or press 'c' to compile).)", + .reset_carry = true, + .max_ai_words = 0, + }, + + { .grants = { - Level::TokenGrant::give("int"), - Level::TokenGrant::give("main"), + Level::TokenGrant::give("#include"), + Level::TokenGrant::give("<stdio.h>"), + Level::TokenGrant::give("printf"), Level::TokenGrant::give("("), - Level::TokenGrant::give("void"), + Level::TokenGrant::give("\"hello\""), Level::TokenGrant::give(")"), - Level::TokenGrant::give("{"), - Level::TokenGrant::give("return"), - Level::TokenGrant::give("0"), Level::TokenGrant::give(";"), - Level::TokenGrant::give("}"), }, - }, - - { - .reset_carry = false, - .max_ai_words = 0, .scene = {}, .goal = Goal::AllOf{{ Goal::CompilePattern{ "int main ( void ) { printf ( \"hello\" ) ; return 0 ; }" }, @@ -253,24 +258,30 @@ So the whole thing reads: Press BUILD to compile, then walk to the RUN pult and press it. Your program runs and its output appears on the Program wall — and that's what finishes this step.)", - .grants = { - Level::TokenGrant::give("#include"), - Level::TokenGrant::give("<stdio.h>"), - Level::TokenGrant::give("printf"), - Level::TokenGrant::give("("), - Level::TokenGrant::give("\"hello\""), - Level::TokenGrant::give(")"), - Level::TokenGrant::give(";"), - }, + .reset_carry = false, + .max_ai_words = 0, }, { - .reset_carry = false, - .max_ai_words = 0, + .grants = { + Level::TokenGrant::give("scanf(\"%d\", &x)"), + Level::TokenGrant::give("int"), + Level::TokenGrant::give("x"), + Level::TokenGrant::give("="), + Level::TokenGrant::give("0"), + Level::TokenGrant::give(";"), + Level::TokenGrant::give(";"), + Level::TokenGrant::give(";"), + Level::TokenGrant::give("\"Your Number is : %d\""), + Level::TokenGrant::give(","), + Level::TokenGrant::give("x"), + Level::TokenGrant::give("42"), + Level::TokenGrant::give("printf(\"Enter a Number:\\n\\t\")"), + }, .scene = Scene::Data{ .ai_seeds = {}, .plates = { - { .pos = { 0.0, World::get_FloorY<double>() + 1.2, -6.0 }, .tag = "" }, + { .tag = "", .pos = { 0.0, World::get_FloorY<double>() + 1.2, -6.0 }, }, }, .platforms = { { .pos = { 0.0, World::get_FloorY<double>() + 0.6, -6.0 }, .half_extents = { 1.0, 0.4, 1.0 } }, @@ -288,30 +299,16 @@ the Program wall — and that's what finishes this step.)", - Press E on a placed word to pull it back off. Have a play, then jump onto the plate to continue.)", - .grants = { - Level::TokenGrant::give("scanf(\"%d\", &x)"), - Level::TokenGrant::give("int"), - Level::TokenGrant::give("x"), - Level::TokenGrant::give("="), - Level::TokenGrant::give("0"), - Level::TokenGrant::give(";"), - Level::TokenGrant::give(";"), - Level::TokenGrant::give(";"), - Level::TokenGrant::give("\"Your Number is : %d\""), - Level::TokenGrant::give(","), - Level::TokenGrant::give("x"), - Level::TokenGrant::give("42"), - Level::TokenGrant::give("printf(\"Enter a Number:\\n\\t\")"), - }, + .reset_carry = false, + .max_ai_words = 0, }, { - .reset_carry = true, - .max_ai_words = 0, + .grants = {}, .scene = Scene::Data{ .ai_seeds = {}, .plates = { - { .pos = { 0.0, World::get_FloorY<double>() + 1.2, 0.0 }, .tag = "" }, + { .tag = "", .pos = { 0.0, World::get_FloorY<double>() + 1.2, 0.0 }, }, }, .platforms = { { .pos = { 0.0, World::get_FloorY<double>() + 0.6, 0.0 }, .half_extents = { 1.0, 0.4, 1.0 } }, @@ -324,8 +321,11 @@ Have a play, then jump onto the plate to continue.)", Jump onto the plate to start the first real level: "Char Copy".)", - .grants = {}, + .reset_carry = true, + .max_ai_words = 0, }, }, + .name = "Tutorial", + .desc = "Learn the controls and the code-building loop.", }; } diff --git a/src/levels/scene.hpp b/src/levels/scene.hpp index 4dd66fd..6987cfb 100644 --- a/src/levels/scene.hpp +++ b/src/levels/scene.hpp @@ -12,43 +12,44 @@ namespace Scene namespace Placed { - struct Word - { - bool required = false; - Vec3 pos {}; + struct Word + { + std::string text; - SDL_FColor color = ConfigColor::floor; - std::string text; - }; + Vec3 pos {}; + SDL_FColor color = ConfigColor::floor; + bool required = false; + }; - struct Plate - { - Vec3 pos; + struct Plate + { + std::string tag; - std::string tag; - }; + Vec3 pos; + }; - struct Platform - { - Vec3 pos; - Vec3 half_extents { 1.0, 0.2, 1.0 }; + struct Platform + { + Vec3 pos; + Vec3 half_extents { 1.0, 0.2, 1.0 }; - SDL_FColor fill_color { 0.45f, 0.45f, 0.65f, 1.0f }; - SDL_FColor wire_color { 0.70f, 0.70f, 0.90f, 1.0f }; - }; + SDL_FColor fill_color { 0.45f, 0.45f, 0.65f, 1.0f }; + SDL_FColor wire_color { 0.70f, 0.70f, 0.90f, 1.0f }; + }; - struct Ai - { - double aggressiveness = 1.0; - Vec3 pos; + struct Ai + { + std::string text; + + Vec3 pos; - std::string text; + Text::Token::Ai::State initial_state = Text::Token::Ai::State::Attack; - Text::Token::Ai::State initial_state = Text::Token::Ai::State::Attack; - }; + double aggressiveness = 1.0; + }; } // namespace Placed:: diff --git a/src/rendering/floor_surface.hpp b/src/rendering/floor_surface.hpp index 1176fbc..aed221c 100644 --- a/src/rendering/floor_surface.hpp +++ b/src/rendering/floor_surface.hpp @@ -20,10 +20,10 @@ protected: DynArray<float> _verts; public: virtual void generate(float world_scale, - float floor_y, - double center_x = -0.5, - double center_y = 0.0, - double width = 3.0) = 0; + float floor_y, + double center_x = -0.5, + double center_y = 0.0, + double width = 3.0) = 0; virtual void draw(GlRenderer& gl, const Camera& cam, const Light& light) const = 0; virtual ~Surface() = default; diff --git a/src/rendering/gl_renderer.hpp b/src/rendering/gl_renderer.hpp index 4e57bee..f67d8bd 100644 --- a/src/rendering/gl_renderer.hpp +++ b/src/rendering/gl_renderer.hpp @@ -23,20 +23,6 @@ Mat4 buildModelMat4(const Vec3& pos, float yaw, float pitch); class GlRenderer { - bool _crosshair_enabled {true}; - int _view_w {1}; - int _view_h {1}; - int _fbo_w {0}; - int _fbo_h {0}; - int _msaa_samples {4}; - int _blur_w {0}; - int _blur_h {0}; - float _bloom_strength { 0.6f }; - float _aspect {1.0f}; - - - Mat4 _proj; - SDL_GLContext _ctx{}; GLuint _prog{}; GLint _uMVP {-1}, @@ -118,6 +104,20 @@ class GlRenderer GLint _uEmissiveColor {-1}; GLint _uEmissiveStrength {-1}; + float _bloom_strength { 0.6f }; + float _aspect {1.0f}; + int _view_w {1}; + int _view_h {1}; + int _fbo_w {0}; + int _fbo_h {0}; + int _msaa_samples {4}; + int _blur_w {0}; + int _blur_h {0}; + bool _crosshair_enabled {true}; + + Mat4 _proj; + SDL_GLContext _ctx{}; + void create_FBO_ (int w, int h); void resize_FBO_ (int w, int h); void destroy_FBO_ (); diff --git a/src/states/game_context.hpp b/src/states/game_context.hpp index c774522..7d39d22 100644 --- a/src/states/game_context.hpp +++ b/src/states/game_context.hpp @@ -19,6 +19,10 @@ namespace Text { class Handler; } struct GameContext { + DynArray<Text::TokenPtr>words; + DynArray<GameObjectPtr> scenery; + DynArray<PultButton*> buttons; + cpputils::unique_ptr<Player> player; cpputils::unique_ptr<Camera> camera; cpputils::unique_ptr<World> world; @@ -28,9 +32,5 @@ struct GameContext Text::Handler* editor_wall { nullptr }; Text::Handler* info_wall { nullptr }; - DynArray<Text::TokenPtr>words; - DynArray<GameObjectPtr> scenery; - DynArray<PultButton*> buttons; - void drop_WordToFloor(Text::TokenPtr word, const Vec3& vel); }; diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp index 2ec1aa0..7e104ff 100644 --- a/src/states/game_state.cpp +++ b/src/states/game_state.cpp @@ -940,28 +940,32 @@ GameState::update_Physics(double dt) for (size_t ti = 0; ti < toks.size(); ++ti) { if (!toks[ti]) continue; wall_text_hits.push_back({ - Wall::Position::NegX, - WALL_TOK_BASE | (h << 28) | (li << 14) | ti, toks[ti]->aabb_Min(), toks[ti]->aabb_Max(), + WALL_TOK_BASE | (h << 28) | (li << 14) | ti, + Wall::Position::NegX, }); } } } for (const Text::Handler::NumberHit& nh : _editor.number_Hitboxes()) - wall_text_hits.push_back({ Wall::Position::NegX, nh.index, - nh.aabb_Min, nh.aabb_Max }); + wall_text_hits.push_back({ + nh.aabb_Min, + nh.aabb_Max, + nh.index, + Wall::Position::NegX, + }); for (size_t li = 0; li < _wall_labels.size(); ++li) { const DynArray<Text::TokenPtr>& words = _wall_labels[li].label.get_Words(); for (size_t wi = 0; wi < words.size(); ++wi) { if (!words[wi]) continue; wall_text_hits.push_back({ - Wall::position_Of(_wall_labels[li].role), - LABEL_HIT_BASE | (li << 16) | wi, words[wi]->aabb_Min(), words[wi]->aabb_Max(), + LABEL_HIT_BASE | (li << 16) | wi, + Wall::position_Of(_wall_labels[li].role), }); } } diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp index 5ce9931..25baeaa 100644 --- a/src/states/game_state.hpp +++ b/src/states/game_state.hpp @@ -33,6 +33,26 @@ class GameState : public IState { + Text::Handler _editor; + Text::Handler _program; + Text::Handler _compiler; + Text::Handler _info; + Wall::Connection _wall_connection; + CarriedLine _carried; + FrameInput _frame_input; + GameContext _ctx; + + DynArray<GameObject*> _dyn_scenery; + DynArray<Text::Token::Data*> _typing_words; + DynArray<Vec3> _freelook_return_from; + DynArray<Projectile> _projectiles; + DynArray<Wall::Label> _wall_labels; + DynArray<WordAnim_Add> _anims_add; + DynArray<WordAnim_Drop> _anims_drop; + DynArray<Text::Handler*> _text_walls; + + std::string _typing_last; + cpputils::unique_ptr<InputSystem> _input_sys; cpputils::unique_ptr<FreelookSystem> _freelook_sys; @@ -54,47 +74,25 @@ class GameState : public IState cpputils::unique_ptr<WordAnimSystem> _word_anim_sys; cpputils::unique_ptr<WordAiSystem> _word_ai_sys; - size_t _carried_last_size { 0 }; double _pick_cooldown { 0.0 }; double _shoot_cooldown { 0.0 }; + // editor-token projectile-hit id range (line numbers use Text::Handler::HIT_BASE) + static constexpr size_t WALL_TOK_BASE = size_t(1) << 40; + static constexpr size_t LABEL_HIT_BASE = size_t(1) << 48; + // focus wall jump START - bool _focus_active { false }; - double _focus_t { 0.0 }; Vec3 _focus_center {}; // look-at point on the wall plane (panned by scroll) Vec3 _focus_normal {}; // inward normal of the focused wall + double _focus_t { 0.0 }; double _focus_dist { 4.0 }; // camera distance from the wall plane (dolly via W/S) double _focus_yaw_r { 0.0 }; float _fov_y { 0.0f }; float _aspect { 0.0f }; + bool _focus_active { false }; // focus wall jump END - - std::string _typing_last; - - CarriedLine _carried; - Text::Handler _editor; - Text::Handler _program; - Text::Handler _compiler; - Text::Handler _info; - Wall::Connection _wall_connection; - FrameInput _frame_input; - GameContext _ctx; - - DynArray<GameObject*> _dyn_scenery; - DynArray<Text::Token::Data*> _typing_words; - DynArray<Vec3> _freelook_return_from; - DynArray<Projectile> _projectiles; - DynArray<Wall::Label> _wall_labels; - DynArray<WordAnim_Add> _anims_add; - DynArray<WordAnim_Drop> _anims_drop; - DynArray<Text::Handler*> _text_walls; - - // editor-token projectile-hit id range (line numbers use Text::Handler::HIT_BASE) - static constexpr size_t WALL_TOK_BASE = size_t(1) << 40; - static constexpr size_t LABEL_HIT_BASE = size_t(1) << 48; - void wallconnect_BeginEditor (); void wallconnect_BeginProgram(); diff --git a/src/states/menu_state.hpp b/src/states/menu_state.hpp index 70ca567..68f13b1 100644 --- a/src/states/menu_state.hpp +++ b/src/states/menu_state.hpp @@ -29,21 +29,23 @@ class MenuState : public IState std::string label; }; + DynArray<MenuItem> _items; + cpputils::unique_ptr<World> _world; cpputils::unique_ptr<Camera> _camera; cpputils::unique_ptr<Light> _light; - Text::TokenPtr _title; - size_t _selected {0}; + Text::TokenPtr _title; + double _scroll_current{0.0}; double _scroll_target {0.0}; double _t {0.0}; - DynArray<MenuItem> _items; + size_t _selected {0}; - void build_Items (); - void activate (Game& game); - bool is_LevelItem(size_t idx) const; - size_t pick_FromCrosshair() const; + void build_Items (); + void activate (Game& game); + bool is_LevelItem (size_t idx) const; + size_t pick_FromCrosshair() const; static uint32_t yaw_FromRadians(double rad); static double yaw_ToRadians (uint32_t yaw); diff --git a/src/systems/aim.hpp b/src/systems/aim.hpp index a7adcf2..3cef6bb 100644 --- a/src/systems/aim.hpp +++ b/src/systems/aim.hpp @@ -17,10 +17,10 @@ namespace Aim struct Result { - Text::Token::Data* track { nullptr }; - Vec3 point {}; Vec3 local {}; + + Text::Token::Data* track { nullptr }; }; diff --git a/src/systems/celebration_system.hpp b/src/systems/celebration_system.hpp index 99f2f72..c805705 100644 --- a/src/systems/celebration_system.hpp +++ b/src/systems/celebration_system.hpp @@ -11,11 +11,11 @@ struct Light; class CelebrationSystem { - bool _active = false; + Text::TokenPtr _banner; double _t = 0, _duration = 3.5, _next_burst = 0; - Text::TokenPtr _banner; + bool _active = false; public: void start (const Camera& cam, double& glyph_yaw); void tick (double dt, ParticleSystem& particle_sys); diff --git a/src/systems/collision.hpp b/src/systems/collision.hpp index 1bb04d6..d0acfaf 100644 --- a/src/systems/collision.hpp +++ b/src/systems/collision.hpp @@ -8,22 +8,22 @@ namespace Collision { struct Manifold { - bool hit = false; - double overlap {}; Vec3 normal {}; + double overlap {}; + bool hit = false; }; struct SweptResult { - bool hit = false; - double t = 0.0; Vec3 normal {}; + double t = 0.0; + bool hit = false; }; void resolve_Objects(GameObject& a, GameObject& b); - bool aabb_Overlap (const GameObject& a, const GameObject& b); - Manifold aabb_Manifold(const GameObject& a, const GameObject& b); - Vec3 reflect (const Vec3& vel, const Vec3& normal, double restitution); - SweptResult swept_RayAabb(const Vec3& origin, const Vec3& dir, - const Vec3& box_min, const Vec3& box_max); + bool aabb_Overlap (const GameObject& a, const GameObject& b); + Manifold aabb_Manifold (const GameObject& a, const GameObject& b); + Vec3 reflect (const Vec3& vel, const Vec3& normal, double restitution); + SweptResult swept_RayAabb (const Vec3& origin, const Vec3& dir, + const Vec3& box_min, const Vec3& box_max); } diff --git a/src/systems/compiler_system.hpp b/src/systems/compiler_system.hpp index 0f1df7a..20564ae 100644 --- a/src/systems/compiler_system.hpp +++ b/src/systems/compiler_system.hpp @@ -14,11 +14,11 @@ struct CompilerSystem { private: - int _count {0}; - std::atomic<bool> _running{false}; - std::mutex _mtx; - std::thread _worker; DynArray<std::string> _pending; + std::thread _worker; + std::mutex _mtx; + std::atomic<bool> _running{false}; + int _count {0}; void push_Line (Text::Handler& out, const std::string& line); static void run_Compile(CompilerSystem* self, std::string src); diff --git a/src/systems/cpp_highlight_system.hpp b/src/systems/cpp_highlight_system.hpp index 934cadf..5b1fe7d 100644 --- a/src/systems/cpp_highlight_system.hpp +++ b/src/systems/cpp_highlight_system.hpp @@ -45,15 +45,15 @@ public: } private: - bool _redo { false }; - std::atomic<bool> _running { false }; - std::atomic<bool> _done { false }; - uint64_t _result_gen { 0 }; - uint64_t _last_gen_pending { 0 }; + DynArray<Text::HiSpan> _pending; + std::string _last_requested; std::thread _worker; std::mutex _mtx; - std::string _last_requested; - DynArray<Text::HiSpan> _pending; + uint64_t _result_gen { 0 }; + uint64_t _last_gen_pending { 0 }; + std::atomic<bool> _running { false }; + std::atomic<bool> _done { false }; + bool _redo { false }; void spawn(const std::string& source, uint64_t gen) { diff --git a/src/systems/dyn_loader_system.hpp b/src/systems/dyn_loader_system.hpp index c4adf16..2da2b8c 100644 --- a/src/systems/dyn_loader_system.hpp +++ b/src/systems/dyn_loader_system.hpp @@ -9,15 +9,14 @@ class DynLoaderSystem { - FILE* _pipe { nullptr }; - void* _handle { nullptr }; + std::string _pending_so; + std::string _last_so_path; int _count { 0 }; int _build_serial { 0 }; - std::string _pending_so; - std::string _last_so_path; - + FILE* _pipe { nullptr }; + void* _handle { nullptr }; void push_Line (Text::Handler& out, const std::string& line); static std::string diff --git a/src/systems/freelook_system.hpp b/src/systems/freelook_system.hpp index de6a668..c620935 100644 --- a/src/systems/freelook_system.hpp +++ b/src/systems/freelook_system.hpp @@ -5,9 +5,9 @@ struct FreelookState { + double lerp_t {}; bool active {}; bool lerping{}; - double lerp_t {}; }; diff --git a/src/systems/hint_system.hpp b/src/systems/hint_system.hpp index c19ece1..8ff6920 100644 --- a/src/systems/hint_system.hpp +++ b/src/systems/hint_system.hpp @@ -11,8 +11,8 @@ class GlRenderer; class HintSystem { - std::string _current_hint; Text::Handler _label; + std::string _current_hint; public: HintSystem (); diff --git a/src/systems/input_system.hpp b/src/systems/input_system.hpp index 228c72b..5116d03 100644 --- a/src/systems/input_system.hpp +++ b/src/systems/input_system.hpp @@ -29,12 +29,23 @@ enum class InputCommand : uint8_t struct FrameInput { - bool forward = false; - bool backward = false; - bool left = false; - bool right = false; - bool pick = false; - bool shoot = false; + DynArray<int> vim_keys; + DynArray<InputCommand> commands; + + std::string typing_buffer; + + double scroll_x = 0.0; + double scroll_y = 0.0; + + int look_x = 0; + int look_y = 0; + + bool forward = false; + bool backward = false; + bool left = false; + bool right = false; + bool pick = false; + bool shoot = false; bool focus_wall = false; bool normal_keys = false; @@ -45,15 +56,6 @@ struct FrameInput bool typing_mode = false; bool typing_commit = false; bool typing_cancel = false; - - int look_x = 0; - int look_y = 0; - double scroll_x = 0.0; - double scroll_y = 0.0; - - std::string typing_buffer; - DynArray<int> vim_keys; - DynArray<InputCommand> commands; }; diff --git a/src/systems/particle_system.hpp b/src/systems/particle_system.hpp index 066e125..1d2239f 100644 --- a/src/systems/particle_system.hpp +++ b/src/systems/particle_system.hpp @@ -13,16 +13,16 @@ class GameObject; struct Particle { - bool alive { true }; - float yaw { 0.0f }; - float spin { 0.0f }; - double age { 0.0 }; - double lifetime { 1.5 }; + SDL_FColor color { 1.0f, 1.0f, 1.0f, 1.0f }; Vec3 pos{}; Vec3 vel{}; - SDL_FColor color { 1.0f, 1.0f, 1.0f, 1.0f }; + double age { 0.0 }; + double lifetime { 1.5 }; + float yaw { 0.0f }; + float spin { 0.0f }; + bool alive { true }; }; diff --git a/src/systems/physics_system.cpp b/src/systems/physics_system.cpp index 2179d59..5017957 100644 --- a/src/systems/physics_system.cpp +++ b/src/systems/physics_system.cpp @@ -412,8 +412,11 @@ PhysicsSystem::resolve_Projectiles(DynArray<Projectile>& projectiles, if (contacts) { contacts->push_back(ProjectileContactEvent{ - .projectile = &proj, .other = obj, - .point = cur, .normal = sw.normal }); + .point = cur, + .normal = sw.normal, + .other = obj, + .projectile = &proj, + }); } } continue; @@ -426,8 +429,11 @@ PhysicsSystem::resolve_Projectiles(DynArray<Projectile>& projectiles, proj.collided = true; if (contacts) { contacts->push_back(ProjectileContactEvent{ - .projectile = &proj, .other = obj, - .point = cur, .normal = sw.normal }); + .point = cur, + .normal = sw.normal, + .other = obj, + .projectile = &proj, + }); } } } diff --git a/src/systems/physics_system.hpp b/src/systems/physics_system.hpp index ccdd35f..8ba9ac9 100644 --- a/src/systems/physics_system.hpp +++ b/src/systems/physics_system.hpp @@ -13,22 +13,21 @@ struct Projectile; struct ProjectileContactEvent { - Projectile* projectile = nullptr; - GameObject* other = nullptr; Vec3 point{}; Vec3 normal{}; + GameObject* other = nullptr; + Projectile* projectile = nullptr; }; class PhysicsSystem { struct SpatialGrid { - int nx = 0, nz = 0; + DynArray<DynArray<GameObject*>> cells; double cell = 2.0; double inv = 0.5; double min_x = 0, min_z = 0; - - DynArray<DynArray<GameObject*>> cells; + int nx = 0, nz = 0; void build(const DynArray<GameObject*>& objs); int index(int cx, int cz) const { return cz * nx + cx; } diff --git a/src/systems/pick_system.hpp b/src/systems/pick_system.hpp index f857185..a1e215a 100644 --- a/src/systems/pick_system.hpp +++ b/src/systems/pick_system.hpp @@ -13,10 +13,11 @@ enum class PickKind : uint8_t { None, Scenery, Word, WallWord }; struct PickResult { size_t word_index { 0 }; - GameObject* scenery_obj { nullptr }; - Text::Handler* wall_handler { nullptr }; - Text::Token::Data* wall_token { nullptr }; - PickKind kind { PickKind::None }; + GameObject* scenery_obj { nullptr }; + Text::Handler* wall_handler { nullptr }; + Text::Token::Data* wall_token { nullptr }; + PickKind kind { PickKind::None }; + bool hit() const { return kind != PickKind::None; } }; diff --git a/src/systems/program_system.hpp b/src/systems/program_system.hpp index ad40e26..5a63777 100644 --- a/src/systems/program_system.hpp +++ b/src/systems/program_system.hpp @@ -11,13 +11,13 @@ struct ProgramSystem { private: - int _out_fd {-1}; - int _in_fd {-1}; - int _count {0}; + std::string _pending; - pid_t _child {-1}; + pid_t _child {-1}; - std::string _pending; + int _out_fd {-1}; + int _in_fd {-1}; + int _count {0}; void push_Line (Text::Handler& out, const std::string& line); void close_Child(); diff --git a/src/systems/projectile_system.hpp b/src/systems/projectile_system.hpp index 4351c95..77201d6 100644 --- a/src/systems/projectile_system.hpp +++ b/src/systems/projectile_system.hpp @@ -22,19 +22,7 @@ enum class ProjectileLanding : uint8_t struct Projectile { - Wall::Position - stick_wall { Wall::Position::PosX }; - bool sticking = false; - bool collided = false; - bool program_stick = false; - uint8_t bounce_state = 0; - float ribbon_start_yaw {}; - float ribbon_target_yaw {}; - double yaw; - double stick_t = 0.0; - double step_dt = 0.0; - - Text::Token::Data* track_obj = nullptr; + DynArray<Text::TokenPtr> words; Vec3 vel{}; Vec3 dir{}; @@ -42,38 +30,54 @@ struct Projectile Vec3 launch_origin{}; Vec3 cam_origin {}; - Vec3 track_local{}; - Vec3 track_last {}; - + Vec3 track_local {}; + Vec3 track_last {}; Vec3 ribbon_origin {}; Vec3 ribbon_start_origin {}; Vec3 ribbon_target_origin{}; - DynArray<Text::TokenPtr> words; + double yaw; + double stick_t = 0.0; + double step_dt = 0.0; + + float ribbon_start_yaw {}; + float ribbon_target_yaw {}; + + Text::Token::Data* track_obj = nullptr; + + bool sticking = false; + bool collided = false; + bool program_stick = false; + uint8_t bounce_state = 0; + + Wall::Position + stick_wall { Wall::Position::PosX }; }; struct WallTextHitTest { - Wall::Position - wall_position; - size_t index; - Vec3 aabb_Min; Vec3 aabb_Max; + + size_t index; + + Wall::Position + wall_position; }; struct ProjectileResult { - Wall::Position - wall_pos = Wall::Position::NegZ; - size_t wall_text_index = 0; + DynArray<Text::TokenPtr> words; + ProjectileLanding landing = ProjectileLanding::None; + Vec3 vel {}; + size_t wall_text_index = 0; - ProjectileLanding landing = ProjectileLanding::None; - DynArray<Text::TokenPtr> words; + Wall::Position + wall_pos = Wall::Position::NegZ; }; diff --git a/src/systems/pult_system.hpp b/src/systems/pult_system.hpp index 17d299c..3f7501e 100644 --- a/src/systems/pult_system.hpp +++ b/src/systems/pult_system.hpp @@ -17,10 +17,10 @@ namespace Text { class Handler; } class PultSystem { + DynArray<Pult*> _pults; + Pult* _connect_program { nullptr }; Pult* _connect_editor { nullptr }; - - DynArray<Pult*> _pults; public: void init (DynArray<GameObjectPtr>& scenery, DynArray<PultButton*>& buttons_out, diff --git a/src/systems/wall_connection.hpp b/src/systems/wall_connection.hpp index 075c434..d3df49b 100644 --- a/src/systems/wall_connection.hpp +++ b/src/systems/wall_connection.hpp @@ -34,9 +34,11 @@ public: private: - bool _cmd_pos_init { false }; - float _cmd_yaw { 0.0f }; - double _blink { 0.0 }; + std::string _cmd_last; + std::string _prog_last; + + Vec3 _cmd_pos {}; + Vec3 _pult_pos{}; Target _target { Target::None }; @@ -45,11 +47,10 @@ private: Text::TokenPtr _cmdline; Text::TokenPtr _prog_input; - Vec3 _cmd_pos {}; - Vec3 _pult_pos{}; + float _cmd_yaw { 0.0f }; + double _blink { 0.0 }; - std::string _cmd_last; - std::string _prog_last; + bool _cmd_pos_init { false }; void rebuild_Cmdline (const std::string& text); void rebuild_ProgramPreview(const std::string& buffer); diff --git a/src/systems/word_anim_system.hpp b/src/systems/word_anim_system.hpp index 733261e..02b8ef1 100644 --- a/src/systems/word_anim_system.hpp +++ b/src/systems/word_anim_system.hpp @@ -7,7 +7,9 @@ struct WordAnim_Add { - bool complete {false}; + Vec3 start_pos; + Vec3 target_pos; + double yaw_start; double yaw_target; double pitch_start {0.0}; @@ -16,16 +18,15 @@ struct WordAnim_Add Text::TokenPtr word; - Vec3 start_pos; - Vec3 target_pos; + bool complete {false}; }; struct WordAnim_Drop { - bool complete{false}; - Text::TokenPtr word; + + bool complete{false}; }; diff --git a/src/text/handler.hpp b/src/text/handler.hpp index 708968a..c481f18 100644 --- a/src/text/handler.hpp +++ b/src/text/handler.hpp @@ -102,11 +102,12 @@ public: EditReq feed_Keys (DynArray<int>& keys, double eye_y); void mark_Saved (); - int cursor_Row () const; - int cursor_Col () const; - int cursor_VisualCol() const; - bool cursor_ToToken (Token::Data* t); - bool cursor_AtImpact(const Vec3& impact); + int cursor_Row () const; + int cursor_Col () const; + int cursor_VisualCol() const; + bool cursor_ToToken (Token::Data* t); + bool cursor_AtImpact (const Vec3& impact); + const std::string& cmdline_Text() const; std::string modeline () const; VimBuffer::Mode cursor_Mode () const; diff --git a/src/text/mesh.hpp b/src/text/mesh.hpp index e8d3663..60180ae 100644 --- a/src/text/mesh.hpp +++ b/src/text/mesh.hpp @@ -19,8 +19,8 @@ struct MeshTri struct TextMesh { DynArray<Vec3> verts; - DynArray<MeshTri> tris; DynArray<Vec3> vertex_normals; + DynArray<MeshTri> tris; DynArray<float> is_front; bool build( diff --git a/src/text/text.hpp b/src/text/text.hpp index ba6bc5a..39ef26f 100644 --- a/src/text/text.hpp +++ b/src/text/text.hpp @@ -64,21 +64,22 @@ Text { DynArray<TokenPtr> tokens; + double bounce_off { 0.0 }; + double bounce_vel { 0.0 }; double y { 0.0 }; double line_height { 0.0 }; double word_spacing { 0.0 }; - int number { -1 }; - bool show_number { false }; - double bounce_off { 0.0 }; - double bounce_vel { 0.0 }; TokenPtr num_left; TokenPtr num_right; + int number { -1 }; + bool show_number { false }; + bool is_Empty() const { return tokens.empty(); } size_t count () const { return tokens.size(); } - double width(double gap) const; + double width (double gap) const; Token::Data* token_At_Byte(size_t off) const; diff --git a/src/text/vim_buffer.hpp b/src/text/vim_buffer.hpp index 232eced..be93dba 100644 --- a/src/text/vim_buffer.hpp +++ b/src/text/vim_buffer.hpp @@ -60,34 +60,28 @@ public: } private: - bool _dirty = false; - unsigned _version = 0; - int _row = 0, _col = 0; - int _count = 0; + struct Snapshot { std::vector<std::string> lines; int row; int col; }; + + std::vector<std::string> _saved; + std::vector<std::string> _lines{ "" }; + std::vector<Snapshot> _undo; + std::vector<Snapshot> _redo; + + Mode _mode = Mode::Normal; + Signal _sig = Signal::None; std::string _pending; std::string _cmd; std::string _status; - - // Yank/delete register (vim's unnamed register). Linewise yanks (yy/dd/cc) - // set _reg_linewise so paste creates new lines; charwise yanks (yw/dw/cw) - // clear it so paste splices inline. Multi-line linewise content is stored - // '\n'-joined. std::string _register; - bool _reg_linewise = false; - // Undo/redo: full-state snapshots (lines + cursor). Grouped vim-style — one - // snapshot per normal-mode edit, and one per insert SESSION (taken when - // insert begins, so all typing until Esc undoes as a unit). Any new edit - // clears the redo stack. - struct Snapshot { std::vector<std::string> lines; int row; int col; }; - std::vector<Snapshot> _undo; - std::vector<Snapshot> _redo; + unsigned _version = 0; + int _row = 0, + _col = 0; + int _count = 0; - Mode _mode = Mode::Normal; - Signal _sig = Signal::None; - std::vector<std::string> _saved; - std::vector<std::string> _lines{ "" }; + bool _dirty = false; + bool _reg_linewise = false; void touch (); void clamp_Col (); diff --git a/src/utils/config.hpp b/src/utils/config.hpp index b06462e..7ff57a8 100644 --- a/src/utils/config.hpp +++ b/src/utils/config.hpp @@ -93,11 +93,12 @@ namespace ConfigText3d static constexpr double hover_amp = 0.3; static constexpr double hover_speed = 1.5; -static constexpr float word_gap = 0.25f; static constexpr double jiggle_stiff = 80.0; static constexpr double jiggle_damp = 9.0; static constexpr double jiggle_max_off = 0.35; static constexpr double kick_strength = 0.18; + +static constexpr float word_gap = 0.25f; } diff --git a/src/utils/light.hpp b/src/utils/light.hpp index 570513b..934a47d 100644 --- a/src/utils/light.hpp +++ b/src/utils/light.hpp @@ -3,15 +3,14 @@ #include "math.hpp" struct Light { - Vec3 sun_direction { 0.3, -0.9, 0.2 }; - Vec3 sun_color { 1.00, 0.95, 0.85 }; - float sun_intensity { 0.0f }; // off for now + Vec3 sun_direction { 0.3, -0.9, 0.2 }; + Vec3 sun_color { 1.00, 0.95, 0.85 }; + Vec3 flashlight_pos { 0.0, 0.0, 0.0 }; + Vec3 flashlight_dir { 0.0, 0.0, -1.0 }; + Vec3 flashlight_color { 1.00, 0.95, 0.80 }; + Vec3 ambient { 0.25, 0.27, 0.30 }; - Vec3 ambient { 0.25, 0.27, 0.30 }; - - Vec3 flashlight_pos { 0.0, 0.0, 0.0 }; - Vec3 flashlight_dir { 0.0, 0.0, -1.0 }; - Vec3 flashlight_color { 1.00, 0.95, 0.80 }; + float sun_intensity { 0.0f }; // off for now float flashlight_range { 40.0f }; float flashlight_inner_cos { 0.85f }; // ≈ 32° full cone float flashlight_outer_cos { 0.50f }; // ≈ 60° cutoff diff --git a/src/wall/label.hpp b/src/wall/label.hpp index 95f186f..061790d 100644 --- a/src/wall/label.hpp +++ b/src/wall/label.hpp @@ -15,10 +15,10 @@ namespace Wall { struct Label { - Wall::Role role; - float face_yaw; - Vec3 anchor; Text::Handler label; + Vec3 anchor; + float face_yaw; + Wall::Role role; static DynArray<Label> generate_All(float world_scale); diff --git a/src/wall/wall.hpp b/src/wall/wall.hpp index 4ca0c64..c020ee9 100644 --- a/src/wall/wall.hpp +++ b/src/wall/wall.hpp @@ -44,12 +44,13 @@ namespace Wall { public: struct Quad { - bool is_editor; + DynArray<float> verts; + DynArray<int> indices; + Role role; SDL_FColor color; + bool is_editor; - DynArray<float> verts; - DynArray<int> indices; }; void generate(float world_scale);