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.gzSome major refractoring and cleanup Move Text stuff
benchmark/bench_ai.cpp | 8 +-
benchmark/bench_text_mesh.cpp | 2 +-
src/cpp_token/cpp_highlight.hpp | 19 -
src/cpp_token/token.hpp | 29 --
src/cpp_token/token_color.hpp | 31 --
src/cpp_token/token_db.hpp | 20 -
src/cpp_token/ts_test.cpp | 21 -
src/entities/player.hpp | 1 -
src/entities/pult.cpp | 2 +-
src/levels/director.cpp | 20 +-
src/levels/level/tutorial.cpp | 2 +-
src/levels/scene.hpp | 4 +-
src/rendering/gl_renderer.cpp | 2 +-
src/states/game_context.cpp | 32 +-
src/states/game_context.hpp | 10 +-
src/states/game_state.cpp | 197 ++++------
src/states/game_state.hpp | 7 +-
src/states/menu_state.cpp | 4 +-
src/states/menu_state.hpp | 6 +-
src/systems/aim.hpp | 16 +-
src/systems/carry_system.cpp | 18 +-
src/systems/carry_system.hpp | 12 +-
src/systems/celebration_system.cpp | 4 +-
src/systems/celebration_system.hpp | 3 +-
src/systems/compiler_system.cpp | 2 +-
src/systems/cpp_highlight_system.hpp | 8 +-
src/systems/dyn_loader_system.cpp | 2 +-
src/systems/game_mod_abi.h | 2 +-
src/systems/hint_system.cpp | 2 +-
src/systems/physics_system.cpp | 20 +-
src/systems/physics_system.hpp | 2 +-
src/systems/pick_system.cpp | 23 +-
src/systems/pick_system.hpp | 25 +-
src/systems/program_system.cpp | 6 +-
src/systems/program_system.hpp | 2 +-
src/systems/projectile_system.cpp | 36 +-
src/systems/projectile_system.hpp | 10 +-
src/systems/wall_connection.cpp | 8 +-
src/systems/wall_connection.hpp | 14 +-
src/systems/word_ai_system.cpp | 86 ++--
src/systems/word_ai_system.hpp | 36 +-
src/systems/word_anim.cpp | 4 +-
src/systems/word_anim.hpp | 14 +-
src/{cpp_token => text}/cpp_highlight.cpp | 47 +--
src/text/handler.cpp | 190 ++++-----
src/text/handler.hpp | 199 ++++------
src/text/line.cpp | 2 +-
src/text/line.hpp | 35 +-
src/text/text.cpp | 91 +++++
src/text/text.hpp | 88 +++++
src/{text_3d => text}/text_mesh.cpp | 0
src/{text_3d => text}/text_mesh.hpp | 0
src/text/token.cpp | 78 ++--
src/text/token.hpp | 251 ++++++------
src/{cpp_token => text}/token_db.cpp | 111 ++++--
src/text_3d/text_3d.hpp | 25 --
src/utils/frame_profiler.hpp | 2 +-
src/utils/globals.hpp | 31 +-
src/utils/text.cpp | 165 --------
src/utils/text.hpp | 53 ---
src/wall/editor.cpp | 626 ------------------------------
src/wall/editor.hpp | 84 ----
src/wall/label.cpp | 10 +-
src/wall/label.hpp | 2 +-
src/wall/text.cpp | 169 --------
src/wall/wall.cpp | 68 ++++
src/wall/wall.hpp | 44 ++-
67 files changed, 992 insertions(+), 2151 deletions(-)
diff --git a/benchmark/bench_ai.cpp b/benchmark/bench_ai.cpp
index 8d8c6fc..afbf0ee 100644
--- a/benchmark/bench_ai.cpp
+++ b/benchmark/bench_ai.cpp
@@ -4,7 +4,7 @@
#include "systems/word_ai_system.hpp"
#include "systems/carry_system.hpp"
#include "entities/player.hpp"
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
// What's the per-frame cost of my AI system, and does it scale with the number of words?
@@ -18,17 +18,17 @@ static void BM_WordAiUpdate(benchmark::State& state)
CarriedLine carried;
- std::vector<Text3dPtr> words;
+ DynArray<Text::TokenPtr> words;
words.reserve(N);
for (size_t i = 0; i < N; ++i) {
double angle = (static_cast<double>(i) / N) * 6.28318;
Vec3 pos { 5.0 * SDL_cos(angle), 0.0, 5.0 * SDL_sin(angle) };
- words.push_back(make_Text3d("hover", Text3d::depth_floor, 0.003f, pos));
+ words.push_back(Text::make_Token("hover", Text::Token::depth_floor, 0.003f, pos));
}
WordAiSystem ai;
- std::vector<Text3dPtr> dropped;
+ DynArray<Text::TokenPtr> dropped;
constexpr double dt = 1.0 / 60.0;
for (auto _ : state) {
diff --git a/benchmark/bench_text_mesh.cpp b/benchmark/bench_text_mesh.cpp
index 029ffcc..02af821 100644
--- a/benchmark/bench_text_mesh.cpp
+++ b/benchmark/bench_text_mesh.cpp
@@ -1,7 +1,7 @@
#include <benchmark/benchmark.h>
#include <string>
-#include "text_3d/text_mesh.hpp"
+#include "text/text_mesh.hpp"
static const std::string FONT = "/usr/share/fonts/dejavu/DejaVuSansMono.ttf";
diff --git a/src/cpp_token/cpp_highlight.hpp b/src/cpp_token/cpp_highlight.hpp
deleted file mode 100644
index 9bd26d7..0000000
--- a/src/cpp_token/cpp_highlight.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#include <string>
-#include "cpp_token/token.hpp"
-#include "utils/globals.hpp"
-
-
-struct HiSpan
-{
- size_t byte_begin;
- size_t byte_end;
- TokenType type;
-};
-
-
-namespace CppHighlight
-{
- DynArray<HiSpan> classify (const std::string& source);
- TokenType classify_Word(const std::string& word);
-}
diff --git a/src/cpp_token/token.hpp b/src/cpp_token/token.hpp
deleted file mode 100644
index 700ff85..0000000
--- a/src/cpp_token/token.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <string>
-
-enum class TokenType : uint8_t
-{
- Delimiter,
- Identifier,
- Keyword,
- Literal,
- Operator,
- Preprocessor,
- Type,
- Function,
- Variable,
- Comment,
- StringLit,
- NumberLit,
- Namespace,
-};
-
-struct Token
-{
- std::string text;
- TokenType type;
- std::string short_desc;
- std::string long_desc;
-};
diff --git a/src/cpp_token/token_color.hpp b/src/cpp_token/token_color.hpp
deleted file mode 100644
index 1127b25..0000000
--- a/src/cpp_token/token_color.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-#include <SDL3/SDL.h>
-#include "token.hpp"
-#include "token_db.hpp"
-#include "text_3d/text_3d.hpp"
-
-namespace TokenColor
-{
-
-inline SDL_FColor
-get(TokenType t)
-{
- switch (t) {
- case TokenType::Keyword: return ConfigColor::orange;
- case TokenType::Type: return ConfigColor::green;
- case TokenType::Identifier: return ConfigColor::light_gray;
- case TokenType::Literal: return ConfigColor::red;
- case TokenType::Operator: return ConfigColor::soft_blue;
- case TokenType::Delimiter: return ConfigColor::slate_blue;
- case TokenType::Preprocessor:return ConfigColor::pink;
- case TokenType::Function: return ConfigColor::blue;
- case TokenType::Variable: return ConfigColor::pale_sky;
- case TokenType::Comment: return ConfigColor::gray;
- case TokenType::StringLit: return ConfigColor::lime;
- case TokenType::NumberLit: return ConfigColor::amber;
- case TokenType::Namespace: return ConfigColor::teal;
- }
- return ConfigColor::white;
-}
-
-}
diff --git a/src/cpp_token/token_db.hpp b/src/cpp_token/token_db.hpp
deleted file mode 100644
index dd7ee79..0000000
--- a/src/cpp_token/token_db.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include <string>
-
-#include "token.hpp"
-#include "utils/globals.hpp"
-
-namespace TokenDB
-{
- struct Desc
- {
- std::string short_desc;
- std::string long_desc;
- };
-
- const char* get_TypeName (TokenType type);
- Desc get_Description(const std::string& text, TokenType type);
- DynArray<std::string>
- get_TestArrayAllKeywords();
-}
diff --git a/src/cpp_token/ts_test.cpp b/src/cpp_token/ts_test.cpp
deleted file mode 100644
index c78c0a6..0000000
--- a/src/cpp_token/ts_test.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <tree_sitter/api.h>
-#include <SDL3/SDL.h>
-#include <string>
-
-extern "C" const TSLanguage* tree_sitter_cpp(void);
-
-void ts_LinkTest()
-{
- TSParser* p = ts_parser_new();
- ts_parser_set_language(p, tree_sitter_cpp());
- const std::string src = "int main(){ return 0; }";
- TSTree* tree = ts_parser_parse_string(p, nullptr, src.c_str(),
- (uint32_t)src.size());
- TSNode root = ts_tree_root_node(tree);
- SDL_Log("tree-sitter root node type: %s", ts_node_type(root));
- char* s = ts_node_string(root);
- SDL_Log("tree-sitter tree: %s", s);
- free(s);
- ts_tree_delete(tree);
- ts_parser_delete(p);
-}
diff --git a/src/entities/player.hpp b/src/entities/player.hpp
index 0411920..02baa0b 100644
--- a/src/entities/player.hpp
+++ b/src/entities/player.hpp
@@ -3,7 +3,6 @@
#include <SDL3/SDL.h>
#include "systems/input_system.hpp"
-#include "text_3d/text_3d.hpp"
#include "utils/globals.hpp"
diff --git a/src/entities/pult.cpp b/src/entities/pult.cpp
index acd19bb..a0624af 100644
--- a/src/entities/pult.cpp
+++ b/src/entities/pult.cpp
@@ -65,7 +65,7 @@ Pult::Pult(const std::string& n, Vec3 world_pos,
, name(n), half_w(half_width), half_d(half_depth)
{
label.set_Billboard (true);
- label.set_Style (ConfigColor::teal, Text3d::depth_wall, 0.0025f);
+ label.set_Style (ConfigColor::teal, Text::Token::depth_wall, 0.0025f);
label.set_Collidable(true);
label.attach_To (&pos, Vec3{ 0.0, ConfigPult::label_height, 0.0 });
label.set_Text (n);
diff --git a/src/levels/director.cpp b/src/levels/director.cpp
index 4b6b08d..99c4922 100644
--- a/src/levels/director.cpp
+++ b/src/levels/director.cpp
@@ -12,7 +12,7 @@
#include "systems/celebration_system.hpp"
#include "systems/particle_system.hpp"
#include "utils/config.hpp"
-#include "utils/text.hpp"
+#include "text/text.hpp"
#include "wall/wall.hpp"
@@ -65,7 +65,7 @@ Director::spawn_Grants(GameContext& ctx)
static_cast<float>(row) * row_gap
};
- Text3dPtr obj = make_Text3d(tokens[i], Text3d::depth_floor, 0.003f, pos);
+ Text::TokenPtr obj = Text::make_Token(tokens[i], Text::Token::depth_floor, 0.003f, pos);
obj->body.hover_height = hover_y;
ctx.words.push_back(std::move(obj));
}
@@ -76,7 +76,7 @@ void
Director::setup_Scene(GameContext& ctx, const Scene::Data& scene)
{
for (const Scene::Placed::Word& pw : scene.words) {
- Text3dPtr t = make_Text3d(pw.text, Text3d::depth_floor, 0.003f, pw.pos, pw.color);
+ Text::TokenPtr t = Text::make_Token(pw.text, Text::Token::depth_floor, 0.003f, pw.pos, pw.color);
t->body.hover_height = pw.pos.y;
ctx.words.push_back(std::move(t));
}
@@ -98,25 +98,25 @@ Director::setup_Scene(GameContext& ctx, const Scene::Data& scene)
}
for (const Scene::Placed::Ai& a : scene.ai_seeds) {
- Text3dPtr t = make_Text3d(a.text, Text3d::depth_floor, 0.003f, a.pos, ConfigColor::floor);
+ Text::TokenPtr t = Text::make_Token(a.text, Text::Token::depth_floor, 0.003f, a.pos, ConfigColor::floor);
t->body.hover_height = a.pos.y;
t->ai.aggressiveness = a.aggressiveness;
switch (a.initial_state) {
- case Text3dAI::State::Attack:
- t->ai.state = Text3dAI::State::Attack;
+ case Text::Token::Ai::State::Attack:
+ t->ai.state = Text::Token::Ai::State::Attack;
t->body.mode = PhysicsMode::AiHover;
t->set_Color(ConfigColor::attack);
t->ai.state_timer = 0.0;
t->ai.phase_timer = 0.5;
break;
- case Text3dAI::State::Flee:
- t->ai.state = Text3dAI::State::Flee;
+ case Text::Token::Ai::State::Flee:
+ t->ai.state = Text::Token::Ai::State::Flee;
t->body.mode = PhysicsMode::AiHover;
t->set_Color(ConfigColor::flee);
break;
- case Text3dAI::State::Idle:
- case Text3dAI::State::Stunned:
+ case Text::Token::Ai::State::Idle:
+ case Text::Token::Ai::State::Stunned:
break;
}
diff --git a/src/levels/level/tutorial.cpp b/src/levels/level/tutorial.cpp
index f60398f..33394eb 100644
--- a/src/levels/level/tutorial.cpp
+++ b/src/levels/level/tutorial.cpp
@@ -173,7 +173,7 @@ Carrying several? Tap F to throw the WHOLE line at once.)",
.aggressiveness = 1.0,
.pos = { -6.0, World::get_FloorY<double>() + 2.0, -6.0 },
.text = "danger",
- .initial_state = Text3dAI::State::Attack,
+ .initial_state = Text::Token::Ai::State::Attack,
},
},
.plates = {},
diff --git a/src/levels/scene.hpp b/src/levels/scene.hpp
index fc6f5be..4dd66fd 100644
--- a/src/levels/scene.hpp
+++ b/src/levels/scene.hpp
@@ -3,7 +3,7 @@
#include <SDL3/SDL.h>
#include <string>
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/globals.hpp"
@@ -47,7 +47,7 @@ namespace Scene
std::string text;
- Text3dAI::State initial_state = Text3dAI::State::Attack;
+ Text::Token::Ai::State initial_state = Text::Token::Ai::State::Attack;
};
} // namespace Placed::
diff --git a/src/rendering/gl_renderer.cpp b/src/rendering/gl_renderer.cpp
index 8743c81..abde45e 100644
--- a/src/rendering/gl_renderer.cpp
+++ b/src/rendering/gl_renderer.cpp
@@ -826,7 +826,7 @@ GlRenderer::draw_MeshColored(const float* verts, int vert_count,
verts, GL_STATIC_DRAW);
// Re-point attributes at _vbo_c — draw_MeshCached may have repointed
- // this shared VAO at a per-Text3d VBO.
+ // this shared VAO at a per-Text::Token::Data VBO.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(3*sizeof(float)));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(6*sizeof(float)));
diff --git a/src/states/game_context.cpp b/src/states/game_context.cpp
index 5b408a2..f83e5ba 100644
--- a/src/states/game_context.cpp
+++ b/src/states/game_context.cpp
@@ -1,41 +1,15 @@
#include <algorithm>
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "states/game_context.hpp"
void
-GameContext::drop_WordToFloor(Text3dPtr word, const Vec3& vel)
+GameContext::drop_WordToFloor(Text::TokenPtr word, const Vec3& vel)
{
if (!word) return;
- word->set_Look(ConfigColor::keep, Text3d::depth_floor);
+ word->set_Look(ConfigColor::keep, Text::Token::depth_floor);
word->body.mode = PhysicsMode::Dynamic;
word->body.vel = vel;
words.push_back(std::move(word));
}
-
-
-void
-GameContext::clear_WallRole(Wall::Role role)
-{
- wall_texts.erase(
- std::remove_if(wall_texts.begin(), wall_texts.end(),
- [role](const Wall::Ptr& wt){ return wt->role == role; }),
- wall_texts.end());
-}
-
-
-void
-GameContext::add_WallText(Wall::Position pos, Wall::Role role, Text3dPtr t)
-{
- wall_texts.push_back(cpputils::make_unique<Wall::Text>(
- Wall::Text{ pos, role, std::move(t) }));
-}
-
-
-void
-GameContext::remove_WallText(size_t index)
-{
- if (index < wall_texts.size())
- wall_texts.erase(wall_texts.begin() + static_cast<std::ptrdiff_t>(index));
-}
diff --git a/src/states/game_context.hpp b/src/states/game_context.hpp
index f0b0065..c774522 100644
--- a/src/states/game_context.hpp
+++ b/src/states/game_context.hpp
@@ -3,7 +3,7 @@
#include <string>
#include "entities/game_object.hpp"
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/globals.hpp"
#include "wall/wall.hpp"
@@ -28,13 +28,9 @@ struct GameContext
Text::Handler* editor_wall { nullptr };
Text::Handler* info_wall { nullptr };
- DynArray<Text3dPtr> words;
+ DynArray<Text::TokenPtr>words;
DynArray<GameObjectPtr> scenery;
DynArray<PultButton*> buttons;
- Wall::PtrList wall_texts;
- void drop_WordToFloor(Text3dPtr word, const Vec3& vel);
- void clear_WallRole (Wall::Role role);
- void add_WallText (Wall::Position pos, Wall::Role role, Text3dPtr t);
- void remove_WallText (size_t index);
+ void drop_WordToFloor(Text::TokenPtr word, const Vec3& vel);
};
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 6db4cf1..bfcc1da 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -10,11 +10,13 @@
#include "rendering/gl_renderer.hpp"
#include "systems/aim.hpp"
#include "systems/celebration_system.hpp"
+#include "systems/cpp_highlight_system.hpp"
#include "systems/game_mod_abi.h"
#include "systems/input_system.hpp"
#include "systems/physics_system.hpp"
#include "systems/pick_system.hpp"
#include "systems/source_tokens.hpp"
+#include "text/token.hpp"
#include "utils/config.hpp"
#include "utils/frame_profiler.hpp"
#include "wall/label.hpp"
@@ -58,7 +60,7 @@ GameState::GameState(size_t starting_level)
Text::LayoutMode::Snapped);
_program.set_Lexing(false);
_program.set_ShowNumbers(false, false);
- _program.set_DefaultStyle(Text3d::depth_wall, 0.003f,
+ _program.set_DefaultStyle(Text::Token::depth_wall, 0.003f,
static_cast<double>(ProgramSystem::line_height),
ConfigWall::free_space_dist);
_program.set_AppendBaseline(static_cast<double>(ProgramSystem::start_y));
@@ -68,7 +70,7 @@ GameState::GameState(size_t starting_level)
Text::LayoutMode::Snapped);
_compiler.set_Lexing(false);
_compiler.set_ShowNumbers(false, false);
- _compiler.set_DefaultStyle(Text3d::depth_compiler, 0.003f,
+ _compiler.set_DefaultStyle(Text::Token::depth_compiler, 0.003f,
static_cast<double>(CompilerSystem::line_height),
ConfigWall::free_space_dist);
_compiler.set_AppendBaseline(static_cast<double>(CompilerSystem::start_y));
@@ -79,7 +81,7 @@ GameState::GameState(size_t starting_level)
Text::LayoutMode::Snapped);
_info.set_Lexing(false);
_info.set_ShowNumbers(false, false);
- _info.set_DefaultStyle(Text3d::depth_wall, 0.003f,
+ _info.set_DefaultStyle(Text::Token::depth_wall, 0.003f,
static_cast<double>(ConfigWall::text_line_spacing),
ConfigWall::free_space_dist);
_info.set_AppendBaseline(World::get_FloorY<double>() + 1.0);
@@ -112,7 +114,7 @@ GameState::GameState(size_t starting_level)
void
GameState::debug_SpawnWords([[maybe_unused]] size_t n)
{
- DynArray<std::string> tokens = TokenDB::get_TestArrayAllKeywords();
+ DynArray<std::string> tokens = Text::Token::get_TestArrayAllKeywords();
static constexpr float spacing = 3.0f;
static constexpr int per_row = 8;
@@ -126,7 +128,7 @@ GameState::debug_SpawnWords([[maybe_unused]] size_t n)
hover_y,
static_cast<float>(i / per_row) * row_gap
};
- Text3dPtr obj = make_Text3d(tokens[i], Text3d::depth_floor, 0.003f, pos);
+ Text::TokenPtr obj = Text::make_Token(tokens[i], Text::Token::depth_floor, 0.003f, pos);
obj->body.hover_height = hover_y;
_ctx.words.push_back(std::move(obj));
}
@@ -192,8 +194,7 @@ GameState::pult_DispatchAction(PultAction act)
void
GameState::on_Click()
{
- PickResult r = PickSystem::pick(*_ctx.camera, _ctx.words, _ctx.scenery,
- _ctx.wall_texts, _text_walls);
+ PickResult r = PickSystem::pick(*_ctx.camera, _ctx.words, _ctx.scenery, _text_walls);
if (!r.hit()) return;
if (r.kind == PickKind::Scenery && r.scenery_obj) {
@@ -206,43 +207,28 @@ GameState::on_Click()
bool
GameState::on_Pick()
{
- PickResult r = PickSystem::pick(*_ctx.camera, _ctx.words, _ctx.scenery,
- _ctx.wall_texts, _text_walls);
+ PickResult r = PickSystem::pick(*_ctx.camera, _ctx.words, _ctx.scenery, _text_walls);
if (!r.hit()) return false;
if (r.kind == PickKind::WallWord && r.wall_handler && r.wall_token) {
- Text3dPtr base = r.wall_handler->detach(r.wall_token);
+ Text::TokenPtr base = r.wall_handler->detach(r.wall_token);
if (!base) return false;
- _level_director->record_PickedUp(base->text);
- base->set_Look(ConfigColor::keep, Text3d::depth_floor);
- Wall::Text::show_TokenInfo(_info, base->text);
- carry_BeginAnim(std::move(base), true);
- return true;
- }
-
- if (r.kind == PickKind::Wall) {
- Text3dPtr base = std::move(_ctx.wall_texts[r.wall_index]->text3d);
- _ctx.remove_WallText(r.wall_index);
- if (!base) return false;
-
- if (base->type == ObjectType::Text) {
- _level_director->record_PickedUp(base->text);
- base->set_Look(ConfigColor::keep, Text3d::depth_floor);
- Wall::Text::show_TokenInfo(_info, base->text);
- }
- carry_BeginAnim(std::move(base), true);
+ _level_director-> record_PickedUp(base->text);
+ base->set_Look (ConfigColor::keep, Text::Token::depth_floor);
+ Wall::show_TokenInfo (_info, base->text);
+ carry_BeginAnim (std::move(base), true);
return true;
}
if (r.kind == PickKind::Word) {
- Text3dPtr word = std::move(_ctx.words[r.word_index]);
+ Text::TokenPtr word = std::move(_ctx.words[r.word_index]);
_ctx.words.erase(_ctx.words.begin() + static_cast<std::ptrdiff_t>(r.word_index));
if (!word) return false;
_level_director->record_PickedUp(word->text);
- word->set_Look(ConfigColor::keep, Text3d::depth_floor);
- Wall::Text::show_TokenInfo(_info, word->text);
+ word->set_Look (ConfigColor::keep, Text::Token::depth_floor);
+ Wall::show_TokenInfo(_info, word->text);
carry_BeginAnim(std::move(word), false);
return true;
@@ -256,7 +242,7 @@ GameState::on_DropLast()
{
if (_carried.empty()) return;
- Text3dPtr word = _carried.pop_back();
+ Text::TokenPtr word = _carried.pop_back();
if (!word) return;
infowall_Clear();
@@ -269,7 +255,7 @@ GameState::on_DropLast()
_anims_drop.push_back(std::move(drop));
if (!_carried.empty())
- Wall::Text::show_TokenInfo(_info, _carried.last->word->text);
+ Wall::show_TokenInfo(_info, _carried.last->word->text);
else
_level_director->show_Details(_ctx);
@@ -282,7 +268,7 @@ GameState::on_ShootOne()
{
if (_carried.empty()) return false;
- Text3dPtr word = _carried.pop_back();
+ Text::TokenPtr word = _carried.pop_back();
if (!word) return false;
infowall_Clear();
@@ -290,16 +276,16 @@ GameState::on_ShootOne()
_freelook_sys->state.active = false;
_freelook_sys->state.lerping = false;
- DynArray<Text3dPtr> single;
+ DynArray<Text::TokenPtr> single;
single.push_back(std::move(word));
Aim::Result aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
- _ctx.wall_texts, _text_walls, World::get_BoxScale());
+ _text_walls, World::get_BoxScale());
_projectiles_sys->launch(_projectiles, std::move(single),
_ctx.camera->fwd, _ctx.camera->pos, aim);
if (!_carried.empty() && _carried.last && _carried.last->word)
- Wall::Text::show_TokenInfo(_info, _carried.last->word->text);
+ Wall::show_TokenInfo(_info, _carried.last->word->text);
else _level_director->show_Details(_ctx);
_level_director->record_Shot();
@@ -317,7 +303,7 @@ GameState::on_ShootAll()
_freelook_sys->state.active = false;
_freelook_sys->state.lerping = false;
- DynArray<Text3dPtr> all_words;
+ DynArray<Text::TokenPtr> all_words;
for (CarriedWordNode* node : _carried.nodes())
if (node->word)
all_words.push_back(std::move(node->word));
@@ -325,13 +311,13 @@ GameState::on_ShootAll()
_carried = CarriedLine();
if (!all_words.empty()) {
Aim::Result aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
- _ctx.wall_texts, _text_walls, World::get_BoxScale());
+ _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();
+ _level_director->record_Shot ();
}
@@ -375,9 +361,6 @@ void
GameState::on_DebugPrint()
{
std::print("TEXT::: {0}\n", editor_Source());
- for (Wall::Ptr& wt : _ctx.wall_texts)
- if (wt->text3d->type == ObjectType::Text)
- SDL_Log("wall text '%s' y=%.3f", wt->text3d->text.c_str(), wt->text3d->pos.y);
}
@@ -413,7 +396,7 @@ void
GameState::on_NumberAdjust(int delta)
{
if (_carried.empty() || !_carried.last || !_carried.last->word) return;
- Text3d* w = _carried.last->word.get();
+ Text::Token::Data* w = _carried.last->word.get();
const std::string& s = w->text;
if (s.empty()) return;
@@ -461,7 +444,7 @@ void
GameState::projectile_FeedProgramInput(ProjectileResult& result)
{
std::string line;
- for (const Text3dPtr& w : result.words) {
+ for (const Text::TokenPtr& w : result.words) {
if (!w || w->type != ObjectType::Text) continue;
if (!line.empty()) line += ' ';
line += w->text;
@@ -476,9 +459,9 @@ GameState::projectile_FeedProgramInput(ProjectileResult& result)
void
GameState::projectile_LandOnFloor(ProjectileResult& result)
{
- for (Text3dPtr& w : result.words) {
+ for (Text::TokenPtr& w : result.words) {
if (!w || w->type != ObjectType::Text) continue;
- w->set_Look(ConfigColor::keep, Text3d::depth_floor);
+ w->set_Look(ConfigColor::keep, Text::Token::depth_floor);
w->body.mode = PhysicsMode::Dynamic;
w->body.vel = result.vel;
_ctx.words.push_back(std::move(w));
@@ -489,41 +472,41 @@ GameState::projectile_LandOnFloor(ProjectileResult& result)
void
GameState::projectile_LandOnWall(ProjectileResult& result)
{
- DynArray<Text3dPtr> editor_block;
+ DynArray<Text::TokenPtr> editor_block;
std::string prog_line, comp_line, info_line;
- for (Text3dPtr& w : result.words) {
+ for (Text::TokenPtr& w : result.words) {
if (!w) continue;
if (w->type == ObjectType::Text)
- w->set_Look(ConfigColor::keep, Text3d::depth_wall);
+ w->set_Look(ConfigColor::keep, Text::Token::depth_wall);
w->body.mode = PhysicsMode::Static;
w->body.vel = Vec3{0, 0, 0};
- Wall::Ptr wt = Wall::Text::from_Drop(std::move(w));
- if (!wt || !wt->text3d) continue;
+ Wall::Drop d = Wall::attach_ToNearestWall(std::move(w));
+ if (!d.token) continue;
- switch (wt->role) {
+ switch (d.role) {
case Wall::Role::Editor:
- editor_block.push_back(std::move(wt->text3d));
+ editor_block.push_back(std::move(d.token));
break;
case Wall::Role::Program:
if (!prog_line.empty()) prog_line += ' ';
- prog_line += wt->text3d->text;
+ prog_line += d.token->text;
break;
case Wall::Role::Compiler:
if (!comp_line.empty()) comp_line += ' ';
- comp_line += wt->text3d->text;
+ comp_line += d.token->text;
break;
case Wall::Role::Info:
if (!info_line.empty()) info_line += ' ';
- info_line += wt->text3d->text;
+ info_line += d.token->text;
break;
}
}
if (!editor_block.empty()) {
Vec3 impact{0, 0, 0};
- for (const Text3dPtr& t : editor_block)
+ for (const Text::TokenPtr& t : editor_block)
{ impact.x += t->pos.x; impact.y += t->pos.y; impact.z += t->pos.z; }
const double inv = 1.0 / static_cast<double>(editor_block.size());
impact.x *= inv; impact.y *= inv; impact.z *= inv;
@@ -536,13 +519,12 @@ GameState::projectile_LandOnWall(ProjectileResult& result)
void
-GameState::projectile_LandOnWallText(ProjectileResult& result,
- DynArray<size_t>& wall_text_to_erase)
+GameState::projectile_LandOnWallText(ProjectileResult& result)
{
const size_t idx = result.wall_text_index;
if (idx >= LABEL_HIT_BASE) {
- for (Text3dPtr& w : result.words)
+ for (Text::TokenPtr& w : result.words)
if (w) _ctx.drop_WordToFloor(std::move(w), result.vel);
return;
}
@@ -559,19 +541,19 @@ GameState::projectile_LandOnWallText(ProjectileResult& result,
if (li < lines.size() && ti < lines[li].tokens.size() && lines[li].tokens[ti]) {
if (hw->edit_Active()) {
const Vec3 impact = lines[li].tokens[ti]->pos;
- DynArray<Text3dPtr> block;
- for (Text3dPtr& w : result.words) if (w) block.push_back(std::move(w));
+ DynArray<Text::TokenPtr> block;
+ for (Text::TokenPtr& w : result.words) if (w) block.push_back(std::move(w));
hw->attach(std::move(block), impact);
return;
}
- if (Text3dPtr orphan = hw->detach(lines[li].tokens[ti].get())) {
+ if (Text::TokenPtr orphan = hw->detach(lines[li].tokens[ti].get())) {
orphan->body.hover_height = World::get_FloorY<double>() + 1.5;
orphan->yaw = 0.0f;
_ctx.drop_WordToFloor(std::move(orphan), Vec3{0.0, 1.5, 0.0});
}
}
}
- for (Text3dPtr& w : result.words)
+ for (Text::TokenPtr& w : result.words)
if (w) _ctx.drop_WordToFloor(std::move(w), result.vel);
return;
}
@@ -585,29 +567,8 @@ GameState::projectile_LandOnWallText(ProjectileResult& result,
return;
}
- walltext_HandleShootDown(result, wall_text_to_erase);
-}
-
-
-void
-GameState::walltext_HandleShootDown(ProjectileResult& result,
- DynArray<size_t>& to_erase)
-{
- if (result.wall_text_index < _ctx.wall_texts.size()) {
- Wall::Ptr& wt = _ctx.wall_texts[result.wall_text_index];
- if (wt && wt->text3d) {
- Text3dPtr orphan = std::move(wt->text3d);
- orphan->body.hover_height = World::get_FloorY<double>() + 1.5;
- orphan->yaw = 0.0f;
- _ctx.drop_WordToFloor(std::move(orphan), Vec3{0.0, 1.5, 0.0});
- to_erase.push_back(result.wall_text_index);
- }
- }
-
- for (Text3dPtr& w : result.words) {
- if (!w) continue;
- _ctx.drop_WordToFloor(std::move(w), result.vel);
- }
+ for (Text::TokenPtr& w : result.words)
+ if (w) _ctx.drop_WordToFloor(std::move(w), result.vel);
}
@@ -705,10 +666,6 @@ GameState::update(double dt, const GlRenderer* gl)
}
{ FP_ZONE("update.post_step");
- for (Wall::Ptr& wt : _ctx.wall_texts) {
- if (!wt || !wt->text3d) continue;
- wt->text3d->pitch = Wall::Text::tilt_TowardEye(wt->text3d->pos, _ctx.camera->pos);
- }
for (Wall::Label& lbl : _wall_labels)
lbl.update(dt, *_ctx.camera);
@@ -763,7 +720,7 @@ GameState::update(double dt, const GlRenderer* gl)
if (_editor.is_Dirty())
_highlight_sys->request(_editor.render_Source(), _editor.generation());
- DynArray<HiSpan> spans;
+ DynArray<Text::HiSpan> spans;
uint64_t gen;
if (_highlight_sys->poll(spans, gen))
_editor.apply_Highlight(spans, gen);
@@ -806,15 +763,10 @@ GameState::render(SDL_Window& window, GlRenderer& renderer)
}
{ FP_ZONE("render.words");
- for (const Text3dPtr& t : _ctx.words)
+ for (const Text::TokenPtr& t : _ctx.words)
if (t) t->draw(renderer, *_ctx.camera, *_ctx.light);
}
- { FP_ZONE("render.wall_texts");
- for (const Wall::Ptr& wt : _ctx.wall_texts)
- wt->draw(renderer, *_ctx.camera, *_ctx.light);
- }
-
{ FP_ZONE("render.wall_labels");
for (const Wall::Label& lbl : _wall_labels)
lbl.draw(renderer, *_ctx.camera, *_ctx.light);
@@ -835,16 +787,16 @@ GameState::render(SDL_Window& window, GlRenderer& renderer)
}
{ FP_ZONE("render.ai.stolen");
- for (const Text3dPtr& t : _ctx.words) {
+ for (const Text::TokenPtr& t : _ctx.words) {
if (!t) continue;
- for (const Text3dPtr& s : t->ai.stolen)
+ for (const Text::TokenPtr& s : t->ai.stolen)
if (s) s->draw(renderer, *_ctx.camera, *_ctx.light);
}
}
{ FP_ZONE("render._projectiles");
for (const Projectile& proj : _projectiles)
- for (const Text3dPtr& wd : proj.words)
+ for (const Text::TokenPtr& wd : proj.words)
if (wd) wd->draw(renderer, *_ctx.camera, *_ctx.light);
}
@@ -879,16 +831,16 @@ GameState::update_WorldSimulation(double dt)
DynArray<GameObject*> colliders;
colliders.reserve(_ctx.scenery.size() + _ctx.words.size());
for (const GameObjectPtr& p : _ctx.scenery) if (p) colliders.push_back(p.get());
- for (const Text3dPtr& p : _ctx.words) if (p) colliders.push_back(p.get());
+ for (const Text::TokenPtr& p : _ctx.words) if (p) colliders.push_back(p.get());
{ FP_ZONE("updateworldsim.phys_objects");
_physics_sys->update_Objects(colliders, dt, 25.0);
}
{ FP_ZONE("updateworldsim.word_ai");
- DynArray<Text3dPtr> ai_dropped;
+ DynArray<Text::TokenPtr> ai_dropped;
_word_ai_sys->update(_ctx.words, *_ctx.player, _carried, ai_dropped, dt);
- for (Text3dPtr& d : ai_dropped) {
+ for (Text::TokenPtr& d : ai_dropped) {
if (d) _ctx.words.push_back(std::move(d));
}
}
@@ -916,18 +868,6 @@ GameState::update_WorldSimulation(double dt)
DynArray<WallTextHitTest> wall_text_hits;
{ FP_ZONE("updateworldsim.build_wall_hits");
- wall_text_hits.reserve(_ctx.wall_texts.size());
- for (size_t i = 0; i < _ctx.wall_texts.size(); ++i) {
- const Wall::Ptr& wt = _ctx.wall_texts[i];
- if (!wt || !wt->text3d) continue;
- wall_text_hits.push_back({
- wt->wall_position,
- i,
- wt->text3d->aabb_Min(),
- wt->text3d->aabb_Max(),
- });
- }
-
for (size_t h = 0; h < _text_walls.size(); ++h) {
Text::Handler* hw = _text_walls[h];
if (!hw) continue;
@@ -951,7 +891,7 @@ GameState::update_WorldSimulation(double dt)
nh.aabb_Min, nh.aabb_Max });
for (size_t li = 0; li < _wall_labels.size(); ++li) {
- const DynArray<Text3dPtr>& words = _wall_labels[li].label.get_Words();
+ 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({
@@ -971,8 +911,6 @@ GameState::update_WorldSimulation(double dt)
_program_sys->is_Running());
}
- DynArray<size_t> wall_text_to_erase;
-
{ FP_ZONE("updateworldsim.projectile_land");
for (ProjectileResult& result : proj_results) {
switch (result.landing) {
@@ -981,16 +919,11 @@ GameState::update_WorldSimulation(double dt)
projectile_FeedProgramInput(result); break;
case ProjectileLanding::Wall: projectile_LandOnWall (result); break;
case ProjectileLanding::WallText:
- projectile_LandOnWallText (result, wall_text_to_erase);
- break;
+ projectile_LandOnWallText (result); break;
case ProjectileLanding::None: break;
}
}
- std::sort(wall_text_to_erase.begin(), wall_text_to_erase.end(), std::greater<size_t>{});
- for (size_t idx : wall_text_to_erase)
- _ctx.remove_WallText(idx);
-
for (const ProjectileContactEvent& ev : projectile_contacts) {
if (std::optional<PultAction> act =
_pult_sys->handle_ProjectileContact(ev, _ctx.buttons); act) {
@@ -999,9 +932,9 @@ GameState::update_WorldSimulation(double dt)
}
if (ev.other && ev.other->type == ObjectType::Text) {
- Text3d* word = static_cast<Text3d*>(ev.other);
+ Text::Token::Data* word = static_cast<Text::Token::Data*>(ev.other);
_level_director->record_AiDefeated(word->text);
- word->ai.state = Text3dAI::State::Stunned;
+ 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);
@@ -1065,7 +998,7 @@ GameState::dyn_Clear()
_ctx.scenery.end());
_ctx.words.erase(
std::remove_if(_ctx.words.begin(), _ctx.words.end(),
- [&set](const Text3dPtr& wp) { return set.count(wp.get()) > 0; }),
+ [&set](const Text::TokenPtr& wp) { return set.count(wp.get()) > 0; }),
_ctx.words.end());
_dyn_scenery.clear();
}
@@ -1088,7 +1021,7 @@ GameState::dyn_Apply(const DynArray<DynObject>& objs)
_ctx.scenery.push_back(std::move(box));
}
else {
- Text3dPtr t = make_Text3d(o.text, Text3d::depth_floor, 0.003f,
+ Text::TokenPtr t = Text::make_Token(o.text, Text::Token::depth_floor, 0.003f,
Vec3{ o.x, o.y, o.z },
SDL_FColor{ o.r, o.g, o.b, 1.0f });
t->body.mode = PhysicsMode::Static;
@@ -1153,7 +1086,7 @@ GameState::typing_RebuildPreview(const std::string& s)
std::string tok;
while (in >> tok) {
- Text3dPtr t = make_Text3d(tok, Text3d::depth_floor, 0.003f,
+ Text::TokenPtr t = Text::make_Token(tok, Text::Token::depth_floor, 0.003f,
anchor, ConfigColor::floor);
t->yaw = static_cast<float>(glyph_yaw);
t->body.mode = PhysicsMode::Carried;
@@ -1176,7 +1109,7 @@ GameState::typing_ClearPreview()
void
-GameState::carry_BeginAnim(Text3dPtr word, bool with_pitch)
+GameState::carry_BeginAnim(Text::TokenPtr word, bool with_pitch)
{
WordAnim_Add anim;
anim.start_pos = word->pos;
diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp
index 7185ecd..2bdd018 100644
--- a/src/states/game_state.hpp
+++ b/src/states/game_state.hpp
@@ -70,7 +70,7 @@ class GameState : public IState
GameContext _ctx;
DynArray<GameObject*> _dyn_scenery;
- DynArray<Text3d*> _typing_words;
+ DynArray<Text::Token::Data*> _typing_words;
DynArray<Vec3> _freelook_return_from;
DynArray<Projectile> _projectiles;
DynArray<Wall::Label> _wall_labels;
@@ -109,7 +109,7 @@ class GameState : public IState
void on_WallConnectSubmit();
void carriedline_Update ();
- void carry_BeginAnim (Text3dPtr word, bool with_pitch);
+ void carry_BeginAnim (Text::TokenPtr word, bool with_pitch);
void level_TryAdvance (double dt);
void typing_ClearPreview ();
void typing_RebuildPreview (const std::string& s);
@@ -117,12 +117,11 @@ class GameState : public IState
void update_WorldSimulation(double dt);
void infowall_Clear ();
- void walltext_HandleShootDown (ProjectileResult& result, DynArray<size_t>& to_erase);
void projectile_FeedProgramInput(ProjectileResult&);
void projectile_LandOnFloor (ProjectileResult&);
void projectile_LandOnWall (ProjectileResult&);
- void projectile_LandOnWallText (ProjectileResult&, DynArray<size_t>& wall_text_to_erase);
+ void projectile_LandOnWallText (ProjectileResult&);
void pult_DispatchAction(PultAction act);
diff --git a/src/states/menu_state.cpp b/src/states/menu_state.cpp
index 7e0e3c1..c921d6e 100644
--- a/src/states/menu_state.cpp
+++ b/src/states/menu_state.cpp
@@ -65,7 +65,7 @@ MenuState::MenuState()
_camera->yaw = 0;
_camera->pitch = 0;
- _title = make_Text3d("Runtime Compiler",
+ _title = Text::make_Token("Runtime Compiler",
TEXT_DEPTH, TITLE_SCALE,
slot_PosForColumn(COLUMN_MID_RAD, TITLE_Y),
COLOR_TITLE);
@@ -114,7 +114,7 @@ MenuState::build_Items()
for (size_t i = 0; i < _items.size(); ++i) {
const auto color = (i == _selected) ? COLOR_SELECTED : COLOR_NORMAL;
- _items[i].text3d = make_Text3d(_items[i].label,
+ _items[i].text3d = Text::make_Token(_items[i].label,
TEXT_DEPTH, ITEM_SCALE,
_items[i].slot_pos,
color);
diff --git a/src/states/menu_state.hpp b/src/states/menu_state.hpp
index 1050634..70ca567 100644
--- a/src/states/menu_state.hpp
+++ b/src/states/menu_state.hpp
@@ -6,7 +6,7 @@
#include "entities/camera.hpp"
#include "entities/world.hpp"
#include "istate.hpp"
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/globals.hpp"
#include "utils/light.hpp"
@@ -24,7 +24,7 @@ class MenuState : public IState
{
ItemKind kind;
size_t level_index = 0;
- Text3dPtr text3d;
+ Text::TokenPtr text3d;
Vec3 slot_pos {};
std::string label;
};
@@ -32,7 +32,7 @@ class MenuState : public IState
cpputils::unique_ptr<World> _world;
cpputils::unique_ptr<Camera> _camera;
cpputils::unique_ptr<Light> _light;
- Text3dPtr _title;
+ Text::TokenPtr _title;
size_t _selected {0};
double _scroll_current{0.0};
double _scroll_target {0.0};
diff --git a/src/systems/aim.hpp b/src/systems/aim.hpp
index 2890238..a7adcf2 100644
--- a/src/systems/aim.hpp
+++ b/src/systems/aim.hpp
@@ -6,17 +6,18 @@
#include "entities/game_object.hpp"
#include "systems/collision.hpp"
#include "text/handler.hpp"
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/globals.hpp"
#include "wall/wall.hpp"
+
namespace Aim
{
struct Result
{
- Text3d* track { nullptr };
+ Text::Token::Data* track { nullptr };
Vec3 point {};
Vec3 local {};
@@ -25,9 +26,8 @@ struct Result
inline Result
crosshair_Target(const Camera& camera,
- const DynArray<Text3dPtr>& words,
+ const DynArray<Text::TokenPtr>& words,
const DynArray<GameObjectPtr>& scenery,
- const DynArray<Wall::Ptr>& wall_texts,
const DynArray<Text::Handler*>& walls,
float world_scale)
{
@@ -53,9 +53,9 @@ crosshair_Target(const Camera& camera,
out.point = Vec3{ eye.x + seg.x, eye.y + seg.y, eye.z + seg.z };
double best_t = 1.0;
- Text3d* win = nullptr;
+ Text::Token::Data* win = nullptr;
- auto test = [&](const Vec3& mn, const Vec3& mx, Text3d* obj) {
+ auto test = [&](const Vec3& mn, const Vec3& mx, Text::Token::Data* obj) {
Collision::SweptResult sw = Collision::swept_RayAabb(eye, seg, mn, mx);
if (sw.hit && sw.t < best_t) {
best_t = sw.t;
@@ -66,12 +66,10 @@ crosshair_Target(const Camera& camera,
}
};
- for (const Text3dPtr& w : words)
+ for (const Text::TokenPtr& w : words)
if (w) test(w->aabb_Min(), w->aabb_Max(), w.get());
for (const GameObjectPtr& s : scenery)
if (s) test(s->aabb_Min(), s->aabb_Max(), nullptr);
- for (const Wall::Ptr& wt : wall_texts)
- if (wt && wt->text3d) test(wt->text3d->aabb_Min(), wt->text3d->aabb_Max(), nullptr);
for (const Text::Handler* h : walls)
if (h)
diff --git a/src/systems/carry_system.cpp b/src/systems/carry_system.cpp
index 2f14312..4642d45 100644
--- a/src/systems/carry_system.cpp
+++ b/src/systems/carry_system.cpp
@@ -10,7 +10,7 @@ CarriedLine::empty() const
void
-CarriedLine::push(Text3dPtr w)
+CarriedLine::push(Text::TokenPtr w)
{
CarriedWordNodePtr node = cpputils::make_unique<CarriedWordNode>(std::move(w));
node->prev = last;
@@ -27,7 +27,7 @@ CarriedLine::push(Text3dPtr w)
}
-Text3dPtr
+Text::TokenPtr
CarriedLine::pop_back()
{
while (last && !last->word) {
@@ -43,7 +43,7 @@ CarriedLine::pop_back()
}
if (!last) return nullptr;
- Text3dPtr word = std::move(last->word);
+ Text::TokenPtr word = std::move(last->word);
if (last->prev) {
last = last->prev;
@@ -58,12 +58,12 @@ CarriedLine::pop_back()
}
-Text3dPtr
+Text::TokenPtr
CarriedLine::pop_front()
{
if (!first) return nullptr;
- Text3dPtr word = std::move(first->word);
+ Text::TokenPtr word = std::move(first->word);
if (first->next) {
first = std::move(first->next);
@@ -110,12 +110,12 @@ CarriedLine::clear()
void
CarriedLine::compact()
{
- DynArray<Text3dPtr> survivors;
+ DynArray<Text::TokenPtr> survivors;
for (CarriedWordNode* node = first.get(); node; node = node->next.get())
if (node->word) survivors.push_back(std::move(node->word));
clear();
- for (Text3dPtr& w : survivors)
+ for (Text::TokenPtr& w : survivors)
push(std::move(w));
}
@@ -163,12 +163,12 @@ CarriedLine::get_SlotPosition(const Vec3& anchor,
double a = 0.5, b = 0.5;
if (nodes[i]->word->type == ObjectType::Text)
- a = static_cast<const Text3d&>(*nodes[i]->word).half_extent;
+ a = static_cast<const Text::Token::Data&>(*nodes[i]->word).half_extent;
else
a = nodes[i]->word->shape.half_extents.x;
if (nodes[i+1]->word->type == ObjectType::Text)
- b = static_cast<const Text3d&>(*nodes[i+1]->word).half_extent;
+ b = static_cast<const Text::Token::Data&>(*nodes[i+1]->word).half_extent;
else
b = nodes[i+1]->word->shape.half_extents.x;
diff --git a/src/systems/carry_system.hpp b/src/systems/carry_system.hpp
index 5ec9995..8c77b79 100644
--- a/src/systems/carry_system.hpp
+++ b/src/systems/carry_system.hpp
@@ -1,16 +1,16 @@
#pragma once
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/globals.hpp"
struct CarriedWordNode
{
- Text3dPtr word;
+ Text::TokenPtr word;
CarriedWordNode* prev{nullptr};
cpputils::unique_ptr<CarriedWordNode> next;
- CarriedWordNode(Text3dPtr w) : word(std::move(w)) {}
+ CarriedWordNode(Text::TokenPtr w) : word(std::move(w)) {}
};
@@ -25,9 +25,9 @@ struct CarriedLine
void compact();
- void push (Text3dPtr w);
- Text3dPtr pop_back ();
- Text3dPtr pop_front();
+ void push (Text::TokenPtr w);
+ Text::TokenPtr pop_back ();
+ Text::TokenPtr pop_front();
DynArray<CarriedWordNode*>
nodes () const;
diff --git a/src/systems/celebration_system.cpp b/src/systems/celebration_system.cpp
index ece95f4..2e65e25 100644
--- a/src/systems/celebration_system.cpp
+++ b/src/systems/celebration_system.cpp
@@ -29,8 +29,8 @@ CelebrationSystem::start(const Camera& cam, double& glyph_yaw)
banner_pos.z = clamp(banner_pos.z, -ws + margin, ws - margin);
banner_pos.y = clamp(banner_pos.y, floor + 1.0, ws - margin);
- _banner = make_Text3d("Success",
- Text3d::depth_floor * 2.0f,
+ _banner = Text::make_Token("Success",
+ Text::Token::depth_floor * 2.0f,
0.012f, banner_pos,
ConfigColor::white);
_banner->yaw = static_cast<float>(glyph_yaw);
diff --git a/src/systems/celebration_system.hpp b/src/systems/celebration_system.hpp
index 9a24e9a..99f2f72 100644
--- a/src/systems/celebration_system.hpp
+++ b/src/systems/celebration_system.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "utils/globals.hpp"
+#include "text/token.hpp"
class GlRenderer;
class ParticleSystem;
@@ -14,7 +15,7 @@ class CelebrationSystem
double _t = 0,
_duration = 3.5,
_next_burst = 0;
- Text3dPtr _banner;
+ Text::TokenPtr _banner;
public:
void start (const Camera& cam, double& glyph_yaw);
void tick (double dt, ParticleSystem& particle_sys);
diff --git a/src/systems/compiler_system.cpp b/src/systems/compiler_system.cpp
index a520713..9bedaa1 100644
--- a/src/systems/compiler_system.cpp
+++ b/src/systems/compiler_system.cpp
@@ -1,7 +1,7 @@
#include <SDL3/SDL.h>
#include "systems/compiler_system.hpp"
-#include "utils/text.hpp"
+#include "text/text.hpp"
CompilerSystem::~CompilerSystem()
diff --git a/src/systems/cpp_highlight_system.hpp b/src/systems/cpp_highlight_system.hpp
index f7a8f89..bbfee6a 100644
--- a/src/systems/cpp_highlight_system.hpp
+++ b/src/systems/cpp_highlight_system.hpp
@@ -5,7 +5,7 @@
#include <string>
#include <thread>
-#include "cpp_token/cpp_highlight.hpp"
+#include "text/text.hpp"
#include "utils/globals.hpp"
@@ -24,7 +24,7 @@ public:
spawn(source, gen);
}
- bool poll(DynArray<HiSpan>& out, uint64_t& out_gen)
+ bool poll(DynArray<Text::HiSpan>& out, uint64_t& out_gen)
{
if (!_done.load()) return false;
@@ -54,7 +54,7 @@ private:
std::thread _worker;
std::mutex _mtx;
std::string _last_requested;
- DynArray<HiSpan> _pending;
+ DynArray<Text::HiSpan> _pending;
void spawn(const std::string& source, uint64_t gen)
{
@@ -63,7 +63,7 @@ private:
_done.store(false);
_last_gen_pending = gen;
_worker = std::thread([this, source, gen]() {
- DynArray<HiSpan> spans = CppHighlight::classify(source);
+ DynArray<Text::HiSpan> spans = Text::CppHighlight::classify(source);
{
std::lock_guard<std::mutex> lk(_mtx);
_pending = std::move(spans);
diff --git a/src/systems/dyn_loader_system.cpp b/src/systems/dyn_loader_system.cpp
index f9fb0c7..e429323 100644
--- a/src/systems/dyn_loader_system.cpp
+++ b/src/systems/dyn_loader_system.cpp
@@ -4,7 +4,7 @@
#include <sys/select.h>
#include "systems/dyn_loader_system.hpp"
-#include "utils/text.hpp"
+#include "text/text.hpp"
// Where game_mod_abi.h is installed at runtime, and the build scratch dir.
diff --git a/src/systems/game_mod_abi.h b/src/systems/game_mod_abi.h
index 3f5be47..72ed3fc 100644
--- a/src/systems/game_mod_abi.h
+++ b/src/systems/game_mod_abi.h
@@ -25,7 +25,7 @@ typedef enum {
} DynObjKind;
/* A flat, POD description of one object to spawn. No pointers, no C++:
- * the library fills these in, the engine constructs the real Text3d /
+ * the library fills these in, the engine constructs the real Text::Token::Data /
* SolidBox from them. Nothing the library allocates outlives dlclose(). */
typedef struct {
int kind; /* DynObjKind */
diff --git a/src/systems/hint_system.cpp b/src/systems/hint_system.cpp
index 27e1b9e..196b4dc 100644
--- a/src/systems/hint_system.cpp
+++ b/src/systems/hint_system.cpp
@@ -7,7 +7,7 @@ HintSystem::HintSystem()
{
_label.set_Centre (Vec3{ 0.0, World::get_FloorY<double>() + 2.5, 0.0 });
_label.set_Billboard (true);
- _label.set_Style (ConfigColor::yellow, Text3d::depth_wall, 0.003f);
+ _label.set_Style (ConfigColor::yellow, Text::Token::depth_wall, 0.003f);
_label.set_Collidable(true);
}
diff --git a/src/systems/physics_system.cpp b/src/systems/physics_system.cpp
index d396f0b..2179d59 100644
--- a/src/systems/physics_system.cpp
+++ b/src/systems/physics_system.cpp
@@ -5,7 +5,7 @@
#include "systems/collision.hpp"
#include "systems/physics_system.hpp"
#include "systems/projectile_system.hpp"
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/config.hpp"
#include "utils/frame_profiler.hpp"
#include "wall/label.hpp"
@@ -265,22 +265,22 @@ PhysicsSystem::resolve_WorldBounds(GameObject& obj)
void
PhysicsSystem::update_Projectiles(DynArray<Projectile>& projectiles, double dt,
- const DynArray<Text3dPtr>& words)
+ const DynArray<Text::TokenPtr>& words)
{
for (Projectile& p : projectiles) {
if (p.sticking) continue;
if (p.words.empty()) continue;
p.step_dt = dt;
- p.prev_centroid = Text3d::words_centroid(p.words);
+ p.prev_centroid = Text::Token::Data::words_centroid(p.words);
if (p.track_obj) {
bool alive = false;
- for (const Text3dPtr& w : words)
+ for (const Text::TokenPtr& w : words)
if (w.get() == p.track_obj) { alive = true; break; }
if (alive) {
- Text3d* o = p.track_obj;
+ Text::Token::Data* o = p.track_obj;
const double cy = SDL_cos(o->yaw), sy = SDL_sin(o->yaw);
const Vec3 live {
o->pos.x + cy * p.track_local.x + sy * p.track_local.z,
@@ -307,7 +307,7 @@ PhysicsSystem::update_Projectiles(DynArray<Projectile>& projectiles, double dt,
p.vel.y -= ConfigPhysics::proj_grav * dt;
}
- for (Text3dPtr& w : p.words) {
+ for (Text::TokenPtr& w : p.words) {
if (!w) continue;
w->pos.x += p.vel.x * dt;
w->pos.y += p.vel.y * dt;
@@ -354,7 +354,7 @@ PhysicsSystem::resolve_SingleBoxCollision(
double push_x = contact.x - cur.x;
double push_y = contact.y - cur.y;
double push_z = contact.z - cur.z;
- for (const Text3dPtr& gw : proj.words) {
+ for (const Text::TokenPtr& gw : proj.words) {
if (!gw) continue;
gw->pos.x += push_x;
gw->pos.y += push_y;
@@ -380,12 +380,12 @@ PhysicsSystem::resolve_Projectiles(DynArray<Projectile>& projectiles,
for (Projectile& proj : projectiles) {
if (proj.sticking || proj.words.empty()) continue;
- Vec3 cur = Text3d::words_centroid(proj.words);
+ Vec3 cur = Text::Token::Data::words_centroid(proj.words);
Vec3 prev = proj.prev_centroid;
Vec3 dir { cur.x - prev.x, cur.y - prev.y, cur.z - prev.z };
Vec3 word_he { 0.3, 0.07, 0.07 };
- for (const Text3dPtr& w : proj.words) {
+ for (const Text::TokenPtr& w : proj.words) {
if (w) { word_he = w->shape.half_extents; break; }
}
@@ -441,7 +441,7 @@ PhysicsSystem::resolve_Projectiles(DynArray<Projectile>& projectiles,
const auto& lw = label->get_Words();
for (size_t i = 0; i < lw.size(); ++i) {
- const Text3dPtr& hw = lw[i];
+ const Text::TokenPtr& hw = lw[i];
if (!hw || !hw->loaded) continue;
Vec3 vel_before = proj.vel;
diff --git a/src/systems/physics_system.hpp b/src/systems/physics_system.hpp
index a5c426c..ccdd35f 100644
--- a/src/systems/physics_system.hpp
+++ b/src/systems/physics_system.hpp
@@ -51,7 +51,7 @@ public:
void update_Objects(const DynArray<GameObject*>& objects,
double dt, double gravity);
void update_Projectiles (DynArray<Projectile>& projectiles, double dt,
- const DynArray<Text3dPtr>& words);
+ const DynArray<Text::TokenPtr>& words);
void resolve_Projectiles(DynArray<Projectile>& projectiles,
const DynArray<GameObject*>* statics,
const DynArray<Text::Handler*>* labels,
diff --git a/src/systems/pick_system.cpp b/src/systems/pick_system.cpp
index 761105d..7db92b7 100644
--- a/src/systems/pick_system.cpp
+++ b/src/systems/pick_system.cpp
@@ -14,7 +14,7 @@ ang_score(const GameObject& obj, const Vec3& eye, const Vec3& fwd)
{
double he;
if (obj.type == ObjectType::Text)
- he = static_cast<double>(static_cast<const Text3d&>(obj).half_extent);
+ he = static_cast<double>(static_cast<const Text::Token::Data&>(obj).half_extent);
else
he = obj.shape.half_extents.x;
@@ -58,9 +58,8 @@ ang_score(const GameObject& obj, const Vec3& eye, const Vec3& fwd)
PickResult
pick(const Camera& camera,
- const DynArray<Text3dPtr>& words,
+ const DynArray<Text::TokenPtr>& words,
const DynArray<GameObjectPtr>& scenery,
- const DynArray<Wall::Ptr>& wall_texts,
const DynArray<Text::Handler*>& walls)
{
const Vec3& eye = camera.pos;
@@ -70,9 +69,9 @@ pick(const Camera& camera,
double best_angle = MAX_ANGLE;
for (size_t i = 0; i < words.size(); ++i) {
- const Text3dPtr& w = words[i];
+ const Text::TokenPtr& w = words[i];
if (!w || !w->interactable) continue;
- if (w->ai.state != Text3dAI::State::Idle) continue;
+ if (w->ai.state != Text::Token::Ai::State::Idle) continue;
if (w->pos.y < floor_y) continue;
double a = ang_score(*w, eye, camera.fwd);
@@ -94,23 +93,11 @@ pick(const Camera& camera,
}
}
- for (size_t i = 0; i < wall_texts.size(); ++i) {
- const Wall::Ptr& wt = wall_texts[i];
- if (!wt || !wt->text3d) continue;
- if (wt->text3d->pos.y < floor_y) continue;
- double a = ang_score(*wt->text3d, eye, camera.fwd);
- if (a >= 0.0 && a < best_angle) {
- best_angle = a;
- result.kind = PickKind::Wall;
- result.wall_index = i;
- }
- }
-
for (Text::Handler* h : walls) {
if (!h || h->edit_Active()) continue;
for (const Text::Line& line : h->get_Lines()) {
for (const Text::TokenPtr& tp : line.tokens) {
- Text::Token* t = tp.get();
+ Text::Token::Data* t = tp.get();
if (!t || !t->interactable) continue;
if (t->pos.y < floor_y) continue;
double a = ang_score(*t, eye, camera.fwd);
diff --git a/src/systems/pick_system.hpp b/src/systems/pick_system.hpp
index 5a94f0c..09063a1 100644
--- a/src/systems/pick_system.hpp
+++ b/src/systems/pick_system.hpp
@@ -3,40 +3,27 @@
#include "entities/camera.hpp"
#include "entities/game_object.hpp"
#include "text/handler.hpp"
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/globals.hpp"
#include "wall/wall.hpp"
-enum class PickKind : uint8_t
-{
- None,
- Scenery,
- Wall,
- Word,
- WallWord,
-};
-
+enum class PickKind : uint8_t { None, Scenery, Word, WallWord };
struct PickResult
{
size_t word_index { 0 };
- size_t wall_index { 0 };
-
GameObject* scenery_obj { nullptr };
Text::Handler* wall_handler { nullptr };
- Text::Token* wall_token { nullptr };
+ Text::Token::Data* wall_token { nullptr };
PickKind kind { PickKind::None };
-
bool hit() const { return kind != PickKind::None; }
};
-
namespace PickSystem
{
PickResult pick(const Camera& camera,
- const DynArray<Text3dPtr>& words,
- const DynArray<GameObjectPtr>& scenery,
- const DynArray<Wall::Ptr>& wall_texts,
- const DynArray<Text::Handler*>& walls);
+ const DynArray<Text::TokenPtr>& words,
+ const DynArray<GameObjectPtr>& scenery,
+ const DynArray<Text::Handler*>& walls);
}
diff --git a/src/systems/program_system.cpp b/src/systems/program_system.cpp
index c26b0ff..f1bbd02 100644
--- a/src/systems/program_system.cpp
+++ b/src/systems/program_system.cpp
@@ -8,7 +8,7 @@
#include "entities/world.hpp"
#include "systems/program_system.hpp"
-#include "utils/text.hpp"
+#include "text/text.hpp"
#include "utils/config.hpp"
@@ -166,14 +166,14 @@ ProgramSystem::echo_Input(Text::Handler& out, const std::string& line)
void
-ProgramSystem::echo_Words(Text::Handler& out, DynArray<Text3dPtr>& words)
+ProgramSystem::echo_Words(Text::Handler& out, DynArray<Text::TokenPtr>& words)
{
if (words.empty()) return;
// The flown word meshes are discarded; we echo their joined text as one
// output line (append handles the scroll + colour/placement).
std::string line;
- for (const Text3dPtr& w : words) {
+ for (const Text::TokenPtr& w : words) {
if (!w || w->type != ObjectType::Text) continue;
if (!line.empty()) line += ' ';
line += w->text;
diff --git a/src/systems/program_system.hpp b/src/systems/program_system.hpp
index 30a17ee..ad40e26 100644
--- a/src/systems/program_system.hpp
+++ b/src/systems/program_system.hpp
@@ -30,7 +30,7 @@ public:
void feed_Input (const std::string& text);
void echo_Input (Text::Handler& out, const std::string& line);
- void echo_Words (Text::Handler& out, DynArray<Text3dPtr>& words);
+ void echo_Words (Text::Handler& out, DynArray<Text::TokenPtr>& words);
bool is_Running () const { return _child > 0; }
diff --git a/src/systems/projectile_system.cpp b/src/systems/projectile_system.cpp
index f4daef9..8f0779a 100644
--- a/src/systems/projectile_system.cpp
+++ b/src/systems/projectile_system.cpp
@@ -83,7 +83,7 @@ tick_Sticking(Projectile& p, double dt, double ws,
const float fy = wall_target_yaw(p.stick_wall);
const double plane = wall_plane_coord(p.stick_wall, ws);
- for (Text3dPtr& w : p.words) {
+ for (Text::TokenPtr& w : p.words) {
if (!w || w->type != ObjectType::Text) continue;
Vec3 target = wall_reconstruct(p.stick_wall, plane,
@@ -111,7 +111,7 @@ check_FloorCeiling(Projectile& p, const Vec3& centroid, double ws,
DynArray<ProjectileResult>& results)
{
double word_he_y = 0.07;
- for (const Text3dPtr& w : p.words) {
+ for (const Text::TokenPtr& w : p.words) {
if (w) { word_he_y = w->shape.half_extents.y; break; }
}
@@ -122,7 +122,7 @@ check_FloorCeiling(Projectile& p, const Vec3& centroid, double ws,
if (centroid.y > ws) {
p.vel.y = bounce_velocity(p.vel.y, 0.75);
- for (Text3dPtr& w : p.words)
+ for (Text::TokenPtr& w : p.words)
if (w) w->pos.y = ws - 0.01;
}
return false;
@@ -171,7 +171,7 @@ check_WallTextIntercept(const Projectile& p, const Vec3& centroid, double dt,
const Vec3 ray_dir = next_centroid - p.prev_centroid;
Vec3 word_he { 0.3, 0.07, 0.07 };
- for (const Text3dPtr& w : p.words) {
+ for (const Text::TokenPtr& w : p.words) {
if (w) { word_he = w->shape.half_extents; break; }
}
@@ -233,7 +233,7 @@ begin_EditorStick(Projectile& p, Wall::Position hit_pos)
const double dot = cr_x * wt_x + cr_z * wt_z;
const double sign = (dot >= 0.0) ? 1.0 : -1.0;
- const Vec3 c_pos = Text3d::words_centroid(p.words);
+ const Vec3 c_pos = Text::Token::Data::words_centroid(p.words);
const double plane = wall_plane_coord(hit_pos, World::get_BoxScale());
const bool x_wall = (hit_pos == Wall::Position::NegX || hit_pos == Wall::Position::PosX);
@@ -254,7 +254,7 @@ begin_EditorStick(Projectile& p, Wall::Position hit_pos)
const double c_tan = wall_tangent(hit_pos, aim.x, aim.z);
const double c_y = aim.y;
- for (Text3dPtr& w : p.words) {
+ for (Text::TokenPtr& w : p.words) {
if (!w || w->type != ObjectType::Text) continue;
w->ribbon_local = {
c_tan + w->ribbon_local.x * sign,
@@ -284,7 +284,7 @@ begin_ProgramStick(Projectile& p, double ws)
const double c_tan = 0.0;
const double c_y = -ws + 1.0;
- for (Text3dPtr& w : p.words) {
+ for (Text::TokenPtr& w : p.words) {
if (!w || w->type != ObjectType::Text) continue;
w->ribbon_local = {
c_tan + w->ribbon_local.x * sign,
@@ -313,14 +313,14 @@ bounce_OffWall(Projectile& p, Wall::Position hit_pos, double ws,
if (x_axis) {
p.vel.x = bounce_velocity(p.vel.x, 0.70);
- for (Text3dPtr& w : p.words) if (w)
+ for (Text::TokenPtr& w : p.words) if (w)
w->pos.x = (hit_pos == Wall::Position::PosX)
? ( ws - ConfigPhysics::stick_dist - 0.05)
: (-ws + ConfigPhysics::stick_dist + 0.05);
}
else {
p.vel.z = bounce_velocity(p.vel.z, 0.70);
- for (Text3dPtr& w : p.words) if (w)
+ for (Text::TokenPtr& w : p.words) if (w)
w->pos.z = (hit_pos == Wall::Position::PosZ)
? ( ws - ConfigPhysics::stick_dist - 0.05)
: (-ws + ConfigPhysics::stick_dist + 0.05);
@@ -332,7 +332,7 @@ bounce_OffWall(Projectile& p, Wall::Position hit_pos, double ws,
static double
editor_AimY(const Projectile& p, double plane_x)
{
- if (SDL_fabs(p.dir.x) <= 1e-6) return Text3d::words_centroid(p.words).y;
+ if (SDL_fabs(p.dir.x) <= 1e-6) return Text::Token::Data::words_centroid(p.words).y;
const double t = (plane_x - p.cam_origin.x) / p.dir.x;
return p.cam_origin.y + p.dir.y * t;
}
@@ -340,7 +340,7 @@ editor_AimY(const Projectile& p, double plane_x)
static double
editor_AimZ(const Projectile& p, double plane_x)
{
- if (SDL_fabs(p.dir.x) <= 1e-6) return Text3d::words_centroid(p.words).z;
+ if (SDL_fabs(p.dir.x) <= 1e-6) return Text::Token::Data::words_centroid(p.words).z;
const double t = (plane_x - p.cam_origin.x) / p.dir.x;
return p.cam_origin.z + p.dir.z * t;
}
@@ -388,7 +388,7 @@ handle_WallHit(Projectile& p, const Vec3& centroid, Wall::Position hit_pos,
void
ProjectileSystem::launch(DynArray<Projectile>& out,
- DynArray<Text3dPtr> words,
+ DynArray<Text::TokenPtr> words,
const Vec3& fwd,
const Vec3& eye,
const Aim::Result& aim)
@@ -397,10 +397,10 @@ ProjectileSystem::launch(DynArray<Projectile>& out,
double yaw = SDL_atan2(-fwd.x, -fwd.z);
- for (Text3dPtr& w : words) {
+ for (Text::TokenPtr& w : words) {
if (!w) continue;
if (w->type == ObjectType::Text)
- w->set_Look(ConfigColor::keep, Text3d::depth_floor);
+ w->set_Look(ConfigColor::keep, Text::Token::depth_floor);
w->yaw = static_cast<float>(yaw);
}
@@ -408,11 +408,11 @@ ProjectileSystem::launch(DynArray<Projectile>& out,
double right_x = SDL_cos(yaw);
double right_z = -SDL_sin(yaw);
- Vec3 centroid = Text3d::words_centroid(words);
+ Vec3 centroid = Text::Token::Data::words_centroid(words);
- for (Text3dPtr& w : words) {
+ for (Text::TokenPtr& w : words) {
if (!w || w->type != ObjectType::Text) continue;
- Text3d& tx = static_cast<Text3d&>(*w);
+ Text::Token::Data& tx = static_cast<Text::Token::Data&>(*w);
double dx = w->pos.x - centroid.x;
double dz = w->pos.z - centroid.z;
tx.ribbon_local = {
@@ -473,7 +473,7 @@ ProjectileSystem::update(DynArray<Projectile>& projectiles,
if (p.words.empty()) { it = projectiles.erase(it); continue; }
- const Vec3 centroid = Text3d::words_centroid(p.words);
+ const Vec3 centroid = Text::Token::Data::words_centroid(p.words);
bool remove = false;
if (check_FloorCeiling(p, centroid, ws, results)) remove = true;
diff --git a/src/systems/projectile_system.hpp b/src/systems/projectile_system.hpp
index f1655c1..4351c95 100644
--- a/src/systems/projectile_system.hpp
+++ b/src/systems/projectile_system.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "systems/aim.hpp"
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/globals.hpp"
#include "wall/wall.hpp"
@@ -34,7 +34,7 @@ struct Projectile
double stick_t = 0.0;
double step_dt = 0.0;
- Text3d* track_obj = nullptr;
+ Text::Token::Data* track_obj = nullptr;
Vec3 vel{};
Vec3 dir{};
@@ -50,7 +50,7 @@ struct Projectile
Vec3 ribbon_start_origin {};
Vec3 ribbon_target_origin{};
- DynArray<Text3dPtr> words;
+ DynArray<Text::TokenPtr> words;
};
@@ -73,14 +73,14 @@ struct ProjectileResult
Vec3 vel {};
ProjectileLanding landing = ProjectileLanding::None;
- DynArray<Text3dPtr> words;
+ DynArray<Text::TokenPtr> words;
};
struct ProjectileSystem
{
void launch(DynArray<Projectile>& out,
- DynArray<Text3dPtr> words,
+ DynArray<Text::TokenPtr> words,
const Vec3& fwd,
const Vec3& eye,
const Aim::Result& aim);
diff --git a/src/systems/wall_connection.cpp b/src/systems/wall_connection.cpp
index f53da0c..92c3bd0 100644
--- a/src/systems/wall_connection.cpp
+++ b/src/systems/wall_connection.cpp
@@ -28,7 +28,7 @@ Connection::begin_Program(const Vec3& pult_pos)
}
void
-Connection::begin_Editor(const Vec3& pult_pos, const ::Text::Handler& editor)
+Connection::begin_Editor(const Vec3& pult_pos, const Text::Handler& editor)
{
_target = Target::Editor;
_pult_pos = pult_pos;
@@ -70,7 +70,7 @@ Connection::rebuild_ProgramPreview(const std::string& buffer)
Vec3 pos = tf.position;
pos.y = ProgramSystem::start_y;
- _prog_input = make_Text3d("> " + buffer, Text3d::depth_wall, 0.003f, pos, ConfigColor::white);
+ _prog_input = Text::make_Token("> " + buffer, Text::Token::depth_wall, 0.003f, pos, ConfigColor::white);
_prog_input->yaw = tf.yaw;
}
@@ -82,7 +82,7 @@ Connection::rebuild_Cmdline(const std::string& text)
if (text.empty()) return;
// Built at origin; render() places it at the eased _cmd_pos each frame.
- _cmdline = make_Text3d(text, Text3d::depth_wall, 0.0016f, Vec3{0,0,0}, ConfigColor::white);
+ _cmdline = Text::make_Token(text, Text::Token::depth_wall, 0.0016f, Vec3{0,0,0}, ConfigColor::white);
}
@@ -190,7 +190,7 @@ Connection::draw_EditorCursor(GlRenderer& gl, const Camera& cam, const Light& li
if (!_editor) return;
if (SDL_fmod(_blink, 0.8) > 0.4) return;
- const ::Text::Handler::MonoMetrics m = _editor->mono_Metrics();
+ const Text::Handler::MonoMetrics m = _editor->mono_Metrics();
if (m.char_w <= 0.0) return; // no mono layout yet
const int row = _editor->cursor_Row();
diff --git a/src/systems/wall_connection.hpp b/src/systems/wall_connection.hpp
index 44fafbe..075c434 100644
--- a/src/systems/wall_connection.hpp
+++ b/src/systems/wall_connection.hpp
@@ -2,7 +2,7 @@
#include <string>
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
class Camera;
@@ -14,10 +14,6 @@ namespace Text { class Handler; }
namespace Wall
{
-// Pure overlay: draws the editing chrome (cmdline / cursor / electric arc) on
-// top of whatever owns the actual text. For the editor it reads cursor + layout
-// state from a Text::Handler that is being vim-edited in place (Mono layout);
-// the Handler draws its own tokens. For the program it owns a small stdin echo.
class Connection
{
public:
@@ -34,7 +30,7 @@ public:
void clear_ProgramPreview();
void begin_Program(const Vec3& pult_pos);
- void begin_Editor (const Vec3& pult_pos, const ::Text::Handler& editor);
+ void begin_Editor (const Vec3& pult_pos, const Text::Handler& editor);
private:
@@ -44,10 +40,10 @@ private:
Target _target { Target::None };
- const ::Text::Handler* _editor { nullptr }; // editor model (Editor target only)
+ const Text::Handler* _editor { nullptr };
- Text3dPtr _cmdline;
- Text3dPtr _prog_input;
+ Text::TokenPtr _cmdline;
+ Text::TokenPtr _prog_input;
Vec3 _cmd_pos {};
Vec3 _pult_pos{};
diff --git a/src/systems/word_ai_system.cpp b/src/systems/word_ai_system.cpp
index 1ec2a9f..8574e9c 100644
--- a/src/systems/word_ai_system.cpp
+++ b/src/systems/word_ai_system.cpp
@@ -1,12 +1,12 @@
#include <SDL3/SDL.h>
#include <cstdlib>
-#include "cpp_token/token_color.hpp"
#include "entities/player.hpp"
#include "entities/world.hpp"
#include "systems/carry_system.hpp"
#include "systems/collision.hpp"
#include "systems/cpp_highlight_system.hpp"
+#include "text/token.hpp"
#include "utils/config.hpp"
#include "word_ai_system.hpp"
@@ -20,61 +20,61 @@ perpendicular_horizontal(const Vec3& v)
void
-WordAiSystem::update(DynArray<Text3dPtr>& words,
+WordAiSystem::update(DynArray<Text::TokenPtr>& words,
const Player& player,
CarriedLine& carried,
- DynArray<Text3dPtr>& dropped,
+ DynArray<Text::TokenPtr>& dropped,
double dt)
{
- DynArray<Text3dPtr> released;
+ DynArray<Text::TokenPtr> released;
int active_ai_count = 0;
- for (const Text3dPtr& w : words) {
+ for (const Text::TokenPtr& w : words) {
if (!w) continue;
- if (w->ai.state != Text3dAI::State::Idle) ++active_ai_count;
+ if (w->ai.state != Text::Token::Ai::State::Idle) ++active_ai_count;
}
const size_t n = words.size();
for (size_t i = 0; i < n; ++i) {
- Text3dPtr& w = words[i];
+ Text::TokenPtr& w = words[i];
if (!w) continue;
- if ( w->ai.state != Text3dAI::State::Idle &&
+ if ( w->ai.state != Text::Token::Ai::State::Idle &&
w->body.mode != PhysicsMode::AiHover &&
w->body.mode != PhysicsMode::Hover) {
- for (Text3dPtr& s : w->ai.stolen) {
+ for (Text::TokenPtr& s : w->ai.stolen) {
if (!s) continue;
s->body.mode = PhysicsMode::Dynamic;
s->body.vel = w->body.vel * 0.5;
- s->set_Color(TokenColor::get(CppHighlight::classify_Word(s->text)));
+ s->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(s->text)));
released.push_back(std::move(s));
}
w->ai.stolen.clear();
- w->ai.state = Text3dAI::State::Idle;
- w->set_Color(TokenColor::get(CppHighlight::classify_Word(w->text)));
+ w->ai.state = Text::Token::Ai::State::Idle;
+ w->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(w->text)));
continue;
}
w->ai.state_timer += dt;
switch (w->ai.state) {
- case Text3dAI::State::Idle: update_Idle (*w, player, active_ai_count, dt); break;
- case Text3dAI::State::Attack: update_Attack (*w, player, carried, words, dropped, released, dt);
+ case Text::Token::Ai::State::Idle: update_Idle (*w, player, active_ai_count, dt); break;
+ case Text::Token::Ai::State::Attack: update_Attack (*w, player, carried, words, dropped, released, dt);
break;
- case Text3dAI::State::Flee: update_Flee (*w, player, released, dt); break;
- case Text3dAI::State::Stunned: update_Stunned(*w, released, dt); break;
+ case Text::Token::Ai::State::Flee: update_Flee (*w, player, released, dt); break;
+ case Text::Token::Ai::State::Stunned: update_Stunned(*w, released, dt); break;
}
update_StolenTrail(*w, dt);
}
- for (Text3dPtr& r : released)
+ for (Text::TokenPtr& r : released)
if (r) words.push_back(std::move(r));
}
void
-WordAiSystem::update_Idle(Text3d& w, const Player& p, int active_ai_count, double dt)
+WordAiSystem::update_Idle(Text::Token::Data& w, const Player& p, int active_ai_count, double dt)
{
if (w.body.mode != PhysicsMode::Hover) return;
@@ -95,10 +95,10 @@ WordAiSystem::update_Idle(Text3d& w, const Player& p, int active_ai_count, doubl
}
-void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carried,
- DynArray<Text3dPtr>& all_words,
- DynArray<Text3dPtr>& dropped,
- DynArray<Text3dPtr>& released,
+void WordAiSystem::update_Attack(Text::Token::Data& w, const Player& p, CarriedLine& carried,
+ DynArray<Text::TokenPtr>& all_words,
+ DynArray<Text::TokenPtr>& dropped,
+ DynArray<Text::TokenPtr>& released,
double dt)
{
const bool has_carry = (!carried.empty() && carried.last && carried.last->word);
@@ -158,7 +158,7 @@ void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carrie
if (!Collision::aabb_Overlap(w, *node->word)) continue;
if (Random::unit() < ConfigAi::steal_probability) {
- Text3dPtr stolen = std::move(node->word);
+ Text::TokenPtr stolen = std::move(node->word);
stolen->body.mode = PhysicsMode::AiHover;
stolen->set_Color(ConfigColor::attack);
w.ai.stolen.push_back(std::move(stolen));
@@ -166,7 +166,7 @@ void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carrie
carried.compact();
break;
} else {
- Text3dPtr knocked = std::move(node->word);
+ Text::TokenPtr knocked = std::move(node->word);
knocked->body.mode = PhysicsMode::Dynamic;
knocked->body.vel = { 0.0, 2.0, 0.0 };
dropped.push_back(std::move(knocked));
@@ -178,10 +178,10 @@ void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carrie
}
}
- for (Text3dPtr& other : all_words) {
+ for (Text::TokenPtr& other : all_words) {
if (!other) continue;
if (other.get() == &w) continue;
- if (other->ai.state != Text3dAI::State::Idle)continue;
+ if (other->ai.state != Text::Token::Ai::State::Idle)continue;
if (other->body.mode != PhysicsMode::Hover) continue;
if (!Collision::aabb_Overlap(w, *other)) continue;
@@ -207,7 +207,7 @@ void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carrie
void
-WordAiSystem::update_Flee(Text3d& w, const Player& p, DynArray<Text3dPtr>& released, double dt)
+WordAiSystem::update_Flee(Text::Token::Data& w, const Player& p, DynArray<Text::TokenPtr>& released, double dt)
{
Vec3 away = w.pos - p.pos;
double dist = Math::length(away);
@@ -252,7 +252,7 @@ WordAiSystem::update_Flee(Text3d& w, const Player& p, DynArray<Text3dPtr>& relea
void
-WordAiSystem::update_Stunned(Text3d& w, DynArray<Text3dPtr>& released, double dt)
+WordAiSystem::update_Stunned(Text::Token::Data& w, DynArray<Text::TokenPtr>& released, double dt)
{
apply_Locomotion(w, Vec3{}, dt);
@@ -263,13 +263,13 @@ WordAiSystem::update_Stunned(Text3d& w, DynArray<Text3dPtr>& released, double dt
}
void
-WordAiSystem::release_StolenWords(Text3d& w, DynArray<Text3dPtr>& out)
+WordAiSystem::release_StolenWords(Text::Token::Data& w, DynArray<Text::TokenPtr>& out)
{
- for (Text3dPtr& s : w.ai.stolen) {
+ for (Text::TokenPtr& s : w.ai.stolen) {
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(TokenColor::get(CppHighlight::classify_Word(s->text)));
+ s->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(s->text)));
out.push_back(std::move(s));
}
w.ai.stolen.clear();
@@ -277,11 +277,11 @@ WordAiSystem::release_StolenWords(Text3d& w, DynArray<Text3dPtr>& out)
void
-WordAiSystem::enter_Attack(Text3d& w)
+WordAiSystem::enter_Attack(Text::Token::Data& w)
{
w.set_Color(ConfigColor::attack);
w.body.mode = PhysicsMode::AiHover;
- w.ai.state = Text3dAI::State::Attack;
+ w.ai.state = Text::Token::Ai::State::Attack;
w.ai.state_timer = 0.0;
w.ai.phase_timer = 0.5 + Random::unit() * 1.5;
w.ai.in_pause = false;
@@ -294,11 +294,11 @@ WordAiSystem::enter_Attack(Text3d& w)
void
-WordAiSystem::enter_Flee(Text3d& w)
+WordAiSystem::enter_Flee(Text::Token::Data& w)
{
w.set_Color(ConfigColor::flee);
w.body.mode = PhysicsMode::AiHover;
- w.ai.state = Text3dAI::State::Flee;
+ w.ai.state = Text::Token::Ai::State::Flee;
w.ai.state_timer = 0.0;
w.ai.phase_timer = 0.3 + Random::unit() * 0.7;
w.ai.in_pause = false;
@@ -307,9 +307,9 @@ WordAiSystem::enter_Flee(Text3d& w)
void
-WordAiSystem::enter_Stunned(Text3d& w)
+WordAiSystem::enter_Stunned(Text::Token::Data& w)
{
- w.ai.state = Text3dAI::State::Stunned;
+ w.ai.state = Text::Token::Ai::State::Stunned;
w.ai.state_timer = 0.0;
w.body.vel = Vec3{};
w.set_Color(ConfigColor::stunned);
@@ -317,18 +317,18 @@ WordAiSystem::enter_Stunned(Text3d& w)
void
-WordAiSystem::enter_Idle(Text3d& w)
+WordAiSystem::enter_Idle(Text::Token::Data& w)
{
- w.ai.state = Text3dAI::State::Idle;
+ w.ai.state = Text::Token::Ai::State::Idle;
w.ai.state_timer = 0.0;
w.body.mode = PhysicsMode::Hover;
w.body.hover_height = w.ai.hover_height_saved;
- w.set_Color(TokenColor::get(CppHighlight::classify_Word(w.text)));
+ w.set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(w.text)));
}
void
-WordAiSystem::apply_Locomotion(Text3d& w, const Vec3& target_vel, double dt)
+WordAiSystem::apply_Locomotion(Text::Token::Data& w, const Vec3& target_vel, double dt)
{
double k = 1.0 - SDL_exp(-ConfigAi::velocity_lerp_rate * dt);
w.body.vel.x += (target_vel.x - w.body.vel.x) * k;
@@ -354,7 +354,7 @@ WordAiSystem::apply_Locomotion(Text3d& w, const Vec3& target_vel, double dt)
void
-WordAiSystem::update_StolenTrail(Text3d& w, double dt)
+WordAiSystem::update_StolenTrail(Text::Token::Data& w, double dt)
{
if (w.ai.stolen.empty()) return;
@@ -386,7 +386,7 @@ WordAiSystem::update_StolenTrail(Text3d& w, double dt)
Vec3 perp = perpendicular_horizontal(w.ai.trail_dir);
double target_yaw = SDL_atan2(-w.ai.trail_dir.x, -w.ai.trail_dir.z);
- for (Text3dPtr& s : w.ai.stolen) {
+ for (Text::TokenPtr& s : w.ai.stolen) {
if (!s) continue;
offset += static_cast<double>(s->half_extent) + 0.18;
diff --git a/src/systems/word_ai_system.hpp b/src/systems/word_ai_system.hpp
index b0d2033..79a7199 100644
--- a/src/systems/word_ai_system.hpp
+++ b/src/systems/word_ai_system.hpp
@@ -1,7 +1,7 @@
#pragma once
-#include "text_3d/text_3d.hpp"
#include "utils/globals.hpp"
+#include "text/token.hpp"
struct CarriedLine;
@@ -12,31 +12,31 @@ class WordAiSystem
{
int _max_active_ai = 5;
- void update_Idle (Text3d& w, const Player& p, int active_ai_count, double dt);
- void update_Attack(Text3d& w, const Player& p, CarriedLine& carried,
- DynArray<Text3dPtr>& all_words,
- DynArray<Text3dPtr>& dropped,
- DynArray<Text3dPtr>& released,
+ void update_Idle (Text::Token::Data& w, const Player& p, int active_ai_count, double dt);
+ void update_Attack(Text::Token::Data& w, const Player& p, CarriedLine& carried,
+ DynArray<Text::TokenPtr>& all_words,
+ DynArray<Text::TokenPtr>& dropped,
+ DynArray<Text::TokenPtr>& released,
double dt);
- void update_Flee (Text3d& w, const Player& p,
- DynArray<Text3dPtr>& released, double dt);
- void update_Stunned (Text3d& w, DynArray<Text3dPtr>& released, double dt);
- void update_StolenTrail(Text3d& w, double dt);
+ void update_Flee (Text::Token::Data& w, const Player& p,
+ DynArray<Text::TokenPtr>& released, double dt);
+ void update_Stunned (Text::Token::Data& w, DynArray<Text::TokenPtr>& released, double dt);
+ void update_StolenTrail(Text::Token::Data& w, double dt);
- void release_StolenWords(Text3d& w, DynArray<Text3dPtr>& out);
+ void release_StolenWords(Text::Token::Data& w, DynArray<Text::TokenPtr>& out);
- void enter_Attack (Text3d& w);
- void enter_Flee (Text3d& w);
- void enter_Stunned (Text3d& w);
- void enter_Idle (Text3d& w);
+ void enter_Attack (Text::Token::Data& w);
+ void enter_Flee (Text::Token::Data& w);
+ void enter_Stunned (Text::Token::Data& w);
+ void enter_Idle (Text::Token::Data& w);
- void apply_Locomotion(Text3d& w, const Vec3& target_vel, double dt);
+ void apply_Locomotion(Text::Token::Data& w, const Vec3& target_vel, double dt);
public:
void set_MaxActiveAi (int n) { _max_active_ai = n; }
- void update (DynArray<Text3dPtr>& words,
+ void update (DynArray<Text::TokenPtr>& words,
const Player& player,
CarriedLine& carried,
- DynArray<Text3dPtr>& dropped,
+ DynArray<Text::TokenPtr>& dropped,
double dt);
};
diff --git a/src/systems/word_anim.cpp b/src/systems/word_anim.cpp
index df56a02..36bf2a2 100644
--- a/src/systems/word_anim.cpp
+++ b/src/systems/word_anim.cpp
@@ -22,7 +22,7 @@ void
WordAnimSystem::update(DynArray<WordAnim_Add>& anims_add,
DynArray<WordAnim_Drop>& anims_drop,
CarriedLine& carried,
- DynArray<Text3dPtr>& words,
+ DynArray<Text::TokenPtr>& words,
double dt)
{
double floor_y = World::get_FloorY<double>();
@@ -72,7 +72,7 @@ WordAnimSystem::update(DynArray<WordAnim_Add>& anims_add,
drop.word->body.vel.y = 0.0;
drop.word->body.mode = PhysicsMode::Dynamic;
if (drop.word->type == ObjectType::Text)
- drop.word->set_Look(ConfigColor::keep, Text3d::depth_floor);
+ drop.word->set_Look(ConfigColor::keep, Text::Token::depth_floor);
words.push_back(std::move(drop.word));
}
}
diff --git a/src/systems/word_anim.hpp b/src/systems/word_anim.hpp
index 500ae93..733261e 100644
--- a/src/systems/word_anim.hpp
+++ b/src/systems/word_anim.hpp
@@ -1,8 +1,8 @@
#pragma once
#include "systems/carry_system.hpp"
-#include "text_3d/text_3d.hpp"
#include "utils/globals.hpp"
+#include "text/token.hpp"
struct WordAnim_Add
@@ -14,7 +14,7 @@ struct WordAnim_Add
double pitch_target {0.0};
double t {0.0};
- Text3dPtr word;
+ Text::TokenPtr word;
Vec3 start_pos;
Vec3 target_pos;
@@ -25,15 +25,15 @@ struct WordAnim_Drop
{
bool complete{false};
- Text3dPtr word;
+ Text::TokenPtr word;
};
struct WordAnimSystem
{
- void update(DynArray<WordAnim_Add>& anims_add,
- DynArray<WordAnim_Drop>& anims_drop,
- CarriedLine& carried,
- DynArray<Text3dPtr>& words,
+ void update(DynArray<WordAnim_Add>& anims_add,
+ DynArray<WordAnim_Drop>& anims_drop,
+ CarriedLine& carried,
+ DynArray<Text::TokenPtr>& words,
double dt);
};
diff --git a/src/cpp_token/cpp_highlight.cpp b/src/text/cpp_highlight.cpp
similarity index 63%
rename from src/cpp_token/cpp_highlight.cpp
rename to src/text/cpp_highlight.cpp
index 07c3645..32b01dd 100644
--- a/src/cpp_token/cpp_highlight.cpp
+++ b/src/text/cpp_highlight.cpp
@@ -3,31 +3,32 @@
#include <tree_sitter/api.h>
#include <unordered_map>
-#include "cpp_token/cpp_highlight.hpp"
+#include "text/text.hpp"
+#include "text/token.hpp"
extern "C" const TSLanguage* tree_sitter_cpp(void);
namespace {
-// Map a node's type string (+ its parent's) to our TokenType.
+// Map a node's type string (+ its parent's) to our Text::Token::Type.
// Returns false if this node should not emit a span (recurse into children).
bool
-classify_Node(TSNode node, TokenType& out)
+classify_Node(TSNode node, Text::Token::Type& out)
{
const char* t = ts_node_type(node);
// Leaf categories that color directly.
- if (std::strcmp(t, "comment") == 0) { out = TokenType::Comment; return true; }
+ if (std::strcmp(t, "comment") == 0) { out = Text::Token::Type::Comment; return true; }
if (std::strcmp(t, "string_literal") == 0 ||
std::strcmp(t, "raw_string_literal") == 0 ||
std::strcmp(t, "char_literal") == 0 ||
- std::strcmp(t, "system_lib_string") == 0) { out = TokenType::StringLit; return true; }
- if (std::strcmp(t, "number_literal") == 0) { out = TokenType::NumberLit; return true; }
+ std::strcmp(t, "system_lib_string") == 0) { out = Text::Token::Type::StringLit; return true; }
+ if (std::strcmp(t, "number_literal") == 0) { out = Text::Token::Type::NumberLit; return true; }
if (std::strcmp(t, "primitive_type") == 0 ||
std::strcmp(t, "type_identifier") == 0 ||
- std::strcmp(t, "sized_type_specifier") == 0) { out = TokenType::Type; return true; }
- if (std::strcmp(t, "namespace_identifier") == 0) { out = TokenType::Namespace; return true; }
- if (std::strcmp(t, "field_identifier") == 0) { out = TokenType::Variable; return true; }
+ std::strcmp(t, "sized_type_specifier") == 0) { out = Text::Token::Type::Type; return true; }
+ if (std::strcmp(t, "namespace_identifier") == 0) { out = Text::Token::Type::Namespace; return true; }
+ if (std::strcmp(t, "field_identifier") == 0) { out = Text::Token::Type::Variable; return true; }
// Identifiers: depend on parent.
if (std::strcmp(t, "identifier") == 0) {
@@ -35,20 +36,20 @@ classify_Node(TSNode node, TokenType& out)
const char* p = ts_node_is_null(parent) ? "" : ts_node_type(parent);
if (std::strcmp(p, "function_declarator") == 0 ||
std::strcmp(p, "call_expression") == 0)
- { out = TokenType::Function; return true; }
- out = TokenType::Variable; return true;
+ { out = Text::Token::Type::Function; return true; }
+ out = Text::Token::Type::Variable; return true;
}
// Preprocessor: the #include/#define lines.
- if (std::strncmp(t, "preproc", 7) == 0) { out = TokenType::Preprocessor; return true; }
+ if (std::strncmp(t, "preproc", 7) == 0) { out = Text::Token::Type::Preprocessor; return true; }
return false; // not a terminal we color — recurse into children
}
void
-walk(TSNode node, DynArray<HiSpan>& out)
+walk(TSNode node, DynArray<Text::HiSpan>& out)
{
- TokenType type;
+ Text::Token::Type type;
if (ts_node_child_count(node) == 0) {
// leaf — classify and emit if recognized
if (classify_Node(node, type))
@@ -67,10 +68,10 @@ walk(TSNode node, DynArray<HiSpan>& out)
} // namespace
-DynArray<HiSpan>
-CppHighlight::classify(const std::string& source)
+DynArray<Text::HiSpan>
+Text::CppHighlight::classify(const std::string& source)
{
- DynArray<HiSpan> spans;
+ DynArray<Text::HiSpan> spans;
TSParser* p = ts_parser_new();
ts_parser_set_language(p, tree_sitter_cpp());
TSTree* tree = ts_parser_parse_string(p, nullptr, source.c_str(),
@@ -82,10 +83,10 @@ CppHighlight::classify(const std::string& source)
}
-TokenType
-CppHighlight::classify_Word(const std::string& word)
+Text::Token::Type
+Text::CppHighlight::classify_Word(const std::string& word)
{
- static std::unordered_map<std::string, TokenType> cache;
+ static std::unordered_map<std::string, Text::Token::Type> cache;
static std::mutex mtx;
{
@@ -95,12 +96,12 @@ CppHighlight::classify_Word(const std::string& word)
}
// First sight: classify this lone token with tree-sitter.
- DynArray<HiSpan> spans = classify(word);
+ DynArray<Text::HiSpan> spans = classify(word);
// Pick the span covering the most of the word (usually the only one).
- TokenType type = TokenType::Identifier;
+ Text::Token::Type type = Text::Token::Type::Identifier;
size_t best_len = 0;
- for (const HiSpan& s : spans) {
+ for (const Text::HiSpan& s : spans) {
size_t len = s.byte_end - s.byte_begin;
if (len > best_len) { best_len = len; type = s.type; }
}
diff --git a/src/text/handler.cpp b/src/text/handler.cpp
index 33481b1..58f399b 100644
--- a/src/text/handler.cpp
+++ b/src/text/handler.cpp
@@ -3,33 +3,36 @@
#include <sstream>
#include <string>
-#include "cpp_token/cpp_highlight.hpp"
-#include "cpp_token/token_color.hpp"
#include "entities/camera.hpp"
#include "entities/world.hpp"
#include "rendering/gl_renderer.hpp"
#include "text/handler.hpp"
+#include "text/text.hpp"
#include "utils/config.hpp"
-#include "wall/wall.hpp" // Wall::Text::tilt_TowardEye, get_Transform
+#include "wall/wall.hpp"
-namespace Text
+
+namespace
+Text
{
-// ── monospace metrics (file-local; from wall_connection.cpp) ──────────────────
-namespace {
+namespace
+{
-double mono_CharWidth()
+double
+mono_CharWidth()
{
static double w = 0.0;
if (w == 0.0) {
TokenPtr m1 = make_Token("M", Token::depth_wall, 0.003f, Vec3{0,0,0});
TokenPtr m2 = make_Token("MM", Token::depth_wall, 0.003f, Vec3{0,0,0});
- w = 2.0 * (double)m2->half_extent - 2.0 * (double)m1->half_extent;
+ w = 2.0 * static_cast<double>(m2->half_extent) - 2.0 * static_cast<double>(m1->half_extent);
}
return w;
}
-double mono_HalfExtentY()
+double
+mono_HalfExtentY()
{
static double h = 0.0;
if (h == 0.0) {
@@ -39,28 +42,29 @@ double mono_HalfExtentY()
return h;
}
-// Orange line-number token (digit classifies as a literal, so pin it Static).
-TokenPtr make_Number(const std::string& s, const Vec3& pos, float yaw, float scale)
+
+TokenPtr
+make_Number(const std::string& s, const Vec3& pos, float yaw, float scale)
{
TokenPtr t = make_Token(s, Token::depth_wall, scale, pos, ConfigColor::orange);
t->yaw = yaw;
- t->set_Color(ConfigColor::orange); // -> ColorSource::Static
+ t->set_Color(ConfigColor::orange);
return t;
}
-void set_NumberText(Token* t, const std::string& s)
+
+void
+set_NumberText(Token::Data* t, const std::string& s)
{
if (t->text == s) return;
t->text = s;
t->rebuild_Mesh();
- t->set_Color(ConfigColor::orange);
+ t->set_Color (ConfigColor::orange);
}
} // namespace
-// ── construction / config ─────────────────────────────────────────────────────
-
Handler::Handler(Wall::Role role, float world_scale, LayoutMode mode)
: _role(role), _layout(mode)
{
@@ -70,6 +74,7 @@ Handler::Handler(Wall::Role role, float world_scale, LayoutMode mode)
_append_color = ConfigColor::dark_gray;
}
+
void
Handler::set_Layout(LayoutMode m)
{
@@ -78,6 +83,7 @@ Handler::set_Layout(LayoutMode m)
relayout();
}
+
void
Handler::set_ShowNumbers(bool on, bool both_columns)
{
@@ -85,6 +91,7 @@ Handler::set_ShowNumbers(bool on, bool both_columns)
_both_columns = both_columns;
}
+
void
Handler::set_DefaultStyle(float depth, float scale, double line_height, double word_spacing)
{
@@ -95,18 +102,17 @@ Handler::set_DefaultStyle(float depth, float scale, double line_height, double w
}
-// ── content ─────────────────────────────────────────────────────────────────
-
void
Handler::clear()
{
- _lines.clear();
- _last_src.clear();
- _label_off.clear();
+ _lines .clear();
+ _last_src .clear();
+ _label_off .clear();
_label_text.clear();
mark_Dirty();
}
+
void
Handler::set_Lines(const DynArray<std::string>& lines)
{
@@ -133,10 +139,10 @@ Handler::set_Lines(const DynArray<std::string>& lines)
mark_Dirty();
}
+
void
Handler::append_Line(const std::string& text)
{
- // Scroll existing rows up, drop the new line at the baseline (program wall).
for (Line& l : _lines) {
l.y += _line_height;
for (TokenPtr& t : l.tokens) if (t) t->pos.y += _line_height;
@@ -149,21 +155,16 @@ Handler::append_Line(const std::string& text)
t->yaw = _tf.yaw;
t->pos = _tf.position;
t->pos.y = _append_y;
- t->set_Color(_append_color); // Static; program output isn't lexed
+ t->set_Color(_append_color);
ln.tokens.push_back(std::move(t));
_lines.push_back(std::move(ln));
}
-// ── ownership transfer ─────────────────────────────────────────────────────────
-
TokenPtr
-Handler::detach(Token* t)
+Handler::detach(Token::Data* t)
{
- // While editing, the buffer is authoritative — picking a word off the wall
- // mid-edit would desync it. PickSystem should skip an edit_Active() handler;
- // refuse here as a backstop.
if (edit_Active()) return nullptr;
for (size_t li = 0; li < _lines.size(); ++li) {
@@ -182,15 +183,19 @@ Handler::detach(Token* t)
return nullptr;
}
+
void
Handler::attach(DynArray<TokenPtr> tokens, const Vec3& impact)
{
if (tokens.empty()) return;
- if (_layout == LayoutMode::Mono) attach_Mono (tokens, impact);
- else attach_Snapped(tokens, impact);
+ if (_layout == LayoutMode::Mono)
+ attach_Mono (tokens, impact);
+ else attach_Snapped(tokens, impact);
+
mark_Dirty();
}
+
void
Handler::clear_ToFloor(DynArray<TokenPtr>& out)
{
@@ -201,10 +206,10 @@ Handler::clear_ToFloor(DynArray<TokenPtr>& out)
t->body.mode = PhysicsMode::Dynamic;
t->body.vel = { 0.0, 1.5, 0.0 };
t->body.hover_height = hover;
- t->set_Color(ConfigColor::floor); // matches old clear_ToFloor (blue)
- t->set_Depth(Token::depth_floor);
- t->set_ColorSource(ColorSource::Lint); // free word: reclassifies on AI idle
- out.push_back(std::move(t));
+ t->set_Color (ConfigColor::floor);
+ t->set_Depth (Token::depth_floor);
+ t->set_ColorSource (ColorSource::Lint);
+ out.push_back (std::move(t));
}
}
_lines.clear();
@@ -212,8 +217,6 @@ Handler::clear_ToFloor(DynArray<TokenPtr>& out)
}
-// ── Snapped placement (ported from Wall::Editor) ───────────────────────────────
-
Handler::Target
Handler::resolve_Target(double y) const
{
@@ -280,6 +283,7 @@ Handler::resolve_Target(double y) const
}
}
+
Line&
Handler::apply_Target(const Target& t)
{
@@ -302,10 +306,6 @@ Handler::apply_Target(const Target& t)
fresh.y = t.new_y;
fresh.line_height = _line_height;
- // Keep _lines ordered top->bottom (descending y). as_Lines / render_Source /
- // numbering all read array order, so the array order IS the document order;
- // a line shot in above existing text must land at the matching index, not
- // at the end (that's what made the extracted source come out reordered).
size_t pos = 0;
while (pos < _lines.size() && _lines[pos].y > fresh.y) ++pos;
auto it = _lines.insert(_lines.begin() + static_cast<std::ptrdiff_t>(pos),
@@ -313,6 +313,7 @@ Handler::apply_Target(const Target& t)
return *it;
}
+
void
Handler::attach_Snapped(DynArray<TokenPtr>& block, const Vec3& impact)
{
@@ -344,8 +345,8 @@ Handler::attach_Snapped(DynArray<TokenPtr>& block, const Vec3& impact)
Target t = resolve_Target(impact.y);
Line& target = apply_Target(t);
- auto left_edge = [](const Token* w){ return w->pos.z + (double)w->half_extent; };
- auto right_edge = [](const Token* w){ return w->pos.z - (double)w->half_extent; };
+ auto left_edge = [](const Token::Data* w){ return w->pos.z + (double)w->half_extent; };
+ auto right_edge = [](const Token::Data* w){ return w->pos.z - (double)w->half_extent; };
size_t insert_index = 0;
for (const TokenPtr& w : target.tokens)
@@ -395,8 +396,6 @@ Handler::attach_Snapped(DynArray<TokenPtr>& block, const Vec3& impact)
}
-// ── Mono shoot-merge ────────────────────────────────────────────────────────────
-
bool
Handler::impact_To_RowCol(const Vec3& impact, int& row, int& col) const
{
@@ -429,6 +428,7 @@ Handler::impact_To_RowCol(const Vec3& impact, int& row, int& col) const
return true;
}
+
void
Handler::attach_Mono(DynArray<TokenPtr>& block, const Vec3& impact)
{
@@ -443,7 +443,7 @@ Handler::attach_Mono(DynArray<TokenPtr>& block, const Vec3& impact)
if (!word.empty()) word += ' ';
word += w->text;
}
- block.clear(); // meshes dropped; re-projection rebuilds them
+ block.clear();
if (word.empty()) return;
_vim->insert_Word_At(row, col, word);
@@ -453,8 +453,6 @@ Handler::attach_Mono(DynArray<TokenPtr>& block, const Vec3& impact)
}
-// ── async lexing (folds in Text::View) ──────────────────────────────────────────
-
void
Handler::mark_Dirty()
{
@@ -462,13 +460,14 @@ Handler::mark_Dirty()
_dirty = true;
}
+
std::string
Handler::render_Source()
{
std::string out;
for (Line& line : _lines) {
for (size_t i = 0; i < line.tokens.size(); ++i) {
- Token* t = line.tokens[i].get();
+ Token::Data* t = line.tokens[i].get();
if (!t) continue;
if (i) out += ' ';
t->byte_begin = out.size();
@@ -478,22 +477,23 @@ Handler::render_Source()
out += '\n';
}
_last_src = out;
- _dirty = false; // dispatched
+ _dirty = false;
return out;
}
+
void
Handler::apply_Highlight(const DynArray<HiSpan>& spans, uint64_t gen)
{
- if (gen != _gen) return; // stale result; a newer edit is pending
+ if (gen != _gen) return;
for (Line& line : _lines) {
for (TokenPtr& tp : line.tokens) {
- Token* t = tp.get();
+ Token::Data* t = tp.get();
if (!t || t->color_src != ColorSource::Lexed) continue;
size_t best = 0;
- TokenType type = TokenType::Identifier;
+ Token::Type type = Token::Type::Identifier;
bool found = false;
for (const HiSpan& s : spans) {
size_t lo = (t->byte_begin > s.byte_begin) ? t->byte_begin : s.byte_begin;
@@ -504,15 +504,13 @@ Handler::apply_Highlight(const DynArray<HiSpan>& spans, uint64_t gen)
}
}
if (found)
- t->color = TokenColor::get(type); // direct: stays ColorSource::Lexed
+ t->color = Token::get_Color(type);
}
}
}
-// ── queries ─────────────────────────────────────────────────────────────────────
-
-Token*
+Token::Data*
Handler::token_At_Byte(size_t off)
{
for (Line& line : _lines)
@@ -522,6 +520,7 @@ Handler::token_At_Byte(size_t off)
return nullptr;
}
+
DynArray<std::string>
Handler::as_Lines() const
{
@@ -539,8 +538,6 @@ Handler::as_Lines() const
}
-// ── layout dispatch ─────────────────────────────────────────────────────────────
-
void
Handler::relayout()
{
@@ -549,10 +546,10 @@ Handler::relayout()
else relayout_Snapped();
}
+
void
Handler::relayout_Snapped()
{
- // Bulk top->bottom placement (was GameState::editor_PlaceLines).
const double x_wall = _tf.position.x;
const double lh = _line_height;
const double gap = 0.5;
@@ -592,12 +589,10 @@ Handler::relayout_Snapped()
}
}
+
void
Handler::relayout_Mono()
{
- // Project the vim buffer onto a monospace grid (was Connection::rebuild_Editor),
- // reusing token meshes for unchanged lines. Coloring is deferred to the async
- // lex (caller marks dirty); this routine is pure geometry + mesh management.
if (!_vim) return;
const auto& blines = _vim->get_Lines();
@@ -715,10 +710,8 @@ Handler::relayout_Mono()
}
-// ── line numbers (Snapped) — ported from Wall::Editor::update ──────────────────
-
void
-Handler::maintain_Numbers(double dt, const Camera& /*cam*/)
+Handler::maintain_Numbers(double dt, const Camera&)
{
if (_lines.empty()) return;
@@ -787,17 +780,18 @@ Handler::maintain_Numbers(double dt, const Camera& /*cam*/)
}
}
+
void
Handler::apply_Tilt(const Camera& cam)
{
- if (_face == FaceMode::None) return; // Mono grid stays flat (cursor math)
+ if (_face == FaceMode::None) return;
// Phase 1 implements the wall case (Pitch toward eye). Yaw/Both (carried,
// billboard labels) arrive with the TextLabel fold-in.
if (_face != FaceMode::Pitch) return;
- auto tilt = [&](Token* t) {
- if (t) t->pitch = Wall::Text::tilt_TowardEye(t->pos, cam.pos);
+ auto tilt = [&](Token::Data* t) {
+ if (t) t->pitch = Wall::tilt_TwardEye(t->pos, cam.pos);
};
for (Line& l : _lines) {
for (TokenPtr& t : l.tokens) tilt(t.get());
@@ -807,8 +801,6 @@ Handler::apply_Tilt(const Camera& cam)
}
-// ── per-frame ─────────────────────────────────────────────────────────────────
-
void
Handler::update(double dt, const Camera& cam)
{
@@ -818,6 +810,7 @@ Handler::update(double dt, const Camera& cam)
apply_Tilt(cam);
}
+
void
Handler::render(GlRenderer& gl, const Camera& cam, const Light& light) const
{
@@ -831,6 +824,7 @@ Handler::render(GlRenderer& gl, const Camera& cam, const Light& light) const
}
}
+
void
Handler::scroll(double delta)
{
@@ -843,8 +837,6 @@ Handler::scroll(double delta)
}
-// ── line-number shooting (Snapped) ──────────────────────────────────────────────
-
DynArray<Handler::NumberHit>
Handler::number_Hitboxes() const
{
@@ -854,7 +846,7 @@ Handler::number_Hitboxes() const
DynArray<NumberHit> out;
out.reserve(_lines.size() * 2);
- auto push_box = [&](const Token* t, size_t i) {
+ auto push_box = [&](const Token::Data* t, size_t i) {
const double hz = (double)t->half_extent + PAD;
const double hy = (double)t->half_extent_y + PAD;
const Vec3 c = t->pos;
@@ -870,6 +862,7 @@ Handler::number_Hitboxes() const
return out;
}
+
double
Handler::scroll_DeltaToEye(size_t line_id, double eye_y) const
{
@@ -877,6 +870,7 @@ Handler::scroll_DeltaToEye(size_t line_id, double eye_y) const
return eye_y - _lines[line_id].y;
}
+
void
Handler::bounce(size_t line_id)
{
@@ -885,8 +879,6 @@ Handler::bounce(size_t line_id)
}
-// ── Mono cursor support ─────────────────────────────────────────────────────────
-
Handler::MonoMetrics
Handler::mono_Metrics() const
{
@@ -900,8 +892,6 @@ Handler::row_Y(int row) const
}
-// ── live editing (Mono) ─────────────────────────────────────────────────────────
-
void
Handler::begin_Edit(double eye_y)
{
@@ -914,7 +904,7 @@ Handler::begin_Edit(double eye_y)
_vim_seen_version = _vim->get_Version();
_eye_y = eye_y;
- _face = FaceMode::None; // flat grid so the cursor math holds
+ _face = FaceMode::None;
_status.clear();
_layout = LayoutMode::Mono;
@@ -922,6 +912,7 @@ Handler::begin_Edit(double eye_y)
mark_Dirty();
}
+
void
Handler::end_Edit()
{
@@ -931,9 +922,10 @@ Handler::end_Edit()
_face = FaceMode::Pitch;
_layout = LayoutMode::Snapped;
- set_Lines(lines); // rebuilds + relayout_Snapped + mark_Dirty
+ set_Lines(lines);
}
+
Handler::EditReq
Handler::feed_Keys(DynArray<int>& keys, double eye_y)
{
@@ -972,20 +964,28 @@ Handler::feed_Keys(DynArray<int>& keys, double eye_y)
return req;
}
+
void
Handler::mark_Saved()
{
if (_vim) _vim->mark_Saved();
}
+
int
-Handler::cursor_Row() const { return _vim ? _vim->get_Row() : 0; }
+Handler::cursor_Row() const
+{ return _vim ? _vim->get_Row() : 0; }
+
int
-Handler::cursor_Col() const { return _vim ? _vim->get_Col() : 0; }
+Handler::cursor_Col()
+const { return _vim ? _vim->get_Col() : 0; }
+
VimBuffer::Mode
-Handler::cursor_Mode() const { return _vim ? _vim->get_Mode() : VimBuffer::Mode::Normal; }
+Handler::cursor_Mode() const
+{ return _vim ? _vim->get_Mode() : VimBuffer::Mode::Normal; }
+
const std::string&
Handler::cmdline_Text() const
@@ -994,8 +994,11 @@ Handler::cmdline_Text() const
return _vim ? _vim->get_Cmdline() : empty;
}
+
const DynArray<std::string>&
-Handler::status_Lines() const { return _status; }
+Handler::status_Lines() const
+{ return _status; }
+
std::string
Handler::modeline() const
@@ -1009,11 +1012,6 @@ Handler::modeline() const
}
-// ───────────────────────────── billboard label ─────────────────────────────
-// Port of TextLabel: one centred line that faces the eye (or a fixed yaw) and
-// jiggles on kicks. The token meshes are built once (relayout_Billboard); the
-// per-frame anchor follow + spring runs in billboard_Update.
-
void
Handler::set_Style(SDL_FColor color, float depth, float scale)
{
@@ -1022,6 +1020,7 @@ Handler::set_Style(SDL_FColor color, float depth, float scale)
_scale = scale;
}
+
void
Handler::set_Centre(const Vec3& p)
{
@@ -1031,6 +1030,7 @@ Handler::set_Centre(const Vec3& p)
_anchor_ptr = nullptr;
}
+
void
Handler::attach_To(const Vec3* anchor, const Vec3& offset)
{
@@ -1040,6 +1040,7 @@ Handler::attach_To(const Vec3* anchor, const Vec3& offset)
_attach_offset = offset;
}
+
void
Handler::detach()
{
@@ -1047,6 +1048,7 @@ Handler::detach()
_anchor_ptr = nullptr;
}
+
void
Handler::set_Text(const std::string& text)
{
@@ -1056,6 +1058,7 @@ Handler::set_Text(const std::string& text)
relayout_Billboard();
}
+
void
Handler::set_Text(const std::string& text, SDL_FColor color, float depth, float scale)
{
@@ -1064,6 +1067,7 @@ Handler::set_Text(const std::string& text, SDL_FColor color, float depth, float
set_Text(text);
}
+
void
Handler::set_Collidable(bool on)
{
@@ -1078,6 +1082,7 @@ Handler::set_Collidable(bool on)
}
}
+
const DynArray<TokenPtr>&
Handler::get_Words() const
{
@@ -1085,6 +1090,7 @@ Handler::get_Words() const
return _lines.empty() ? empty : _lines[0].tokens;
}
+
Vec3
Handler::label_Centre() const
{
@@ -1095,6 +1101,7 @@ Handler::label_Centre() const
return _centre;
}
+
void
Handler::relayout_Billboard()
{
@@ -1139,6 +1146,7 @@ Handler::relayout_Billboard()
_lines.push_back(std::move(line));
}
+
void
Handler::billboard_Update(double dt, const Camera& cam)
{
@@ -1172,7 +1180,7 @@ Handler::billboard_Update(double dt, const Camera& cam)
auto clamp = [](double v, double lo, double hi) { return v < lo ? lo : (v > hi ? hi : v); };
for (size_t i = 0; i < words.size() && i < _label_off.size(); ++i) {
- Token* t = words[i].get();
+ Token::Data* t = words[i].get();
if (!t || !t->loaded) continue;
const double off = _label_off[i];
@@ -1201,6 +1209,7 @@ Handler::billboard_Update(double dt, const Camera& cam)
}
}
+
void
Handler::kick(size_t word_index, const Vec3& projectile_vel, const Vec3& normal)
{
@@ -1220,4 +1229,5 @@ Handler::kick(size_t word_index, const Vec3& projectile_vel, const Vec3& normal)
v.z += -normal.z * mag;
}
+
} // namespace Text
diff --git a/src/text/handler.hpp b/src/text/handler.hpp
index d52fb21..555ab51 100644
--- a/src/text/handler.hpp
+++ b/src/text/handler.hpp
@@ -1,60 +1,36 @@
#pragma once
-//
-// text/handler.hpp — Phase 1 contract
-//
-// Text::Handler is the document: the single source of truth for one wall's text.
-// It OWNS its lines/tokens (no more reconstruct-by-sorting in extract_Text, no
-// more parallel Wall::Editor / Text::View / EditorRow). It does layout, async
-// lexing, rotation-to-eye, line numbers, scrolling, render, and the ownership
-// transfer (detach/attach) that the pick/projectile mechanics ride on.
-//
-// One Handler per wall role. Per the agreed split, role-SPECIFIC behavior stays
-// in the systems (program_system feeds lines, compiler writes output,
-// show_TokenInfo writes info rows); the Handler is the layout/lex/render engine
-// those systems push content into.
-//
-// Editor + vim are TWO LAYOUTS of the same lines, never simultaneous:
-// LayoutMode::Snapped — shoot-and-snap placement (resolve_Target/place_OnLine).
-// LayoutMode::Mono — monospace grid; what vim edits on.
-// Switching modes repositions tokens and rebuilds meshes only for changed text,
-// killing the current editor_Clear()+editor_PlaceLines() thrash.
-//
#include <cstdint>
#include <string>
-#include "cpp_token/cpp_highlight.hpp" // HiSpan
#include "entities/camera.hpp"
+#include "text/text.hpp"
#include "text/line.hpp"
#include "text/token.hpp"
-#include "text/vim_buffer.hpp" // VimBuffer (owned in Mono mode)
+#include "text/vim_buffer.hpp"
#include "utils/globals.hpp"
#include "utils/light.hpp"
-#include "wall/wall.hpp" // Wall::Role, Wall::Transform, Wall::Position
+#include "wall/wall.hpp"
class GlRenderer;
namespace Text
{
-enum class LayoutMode : uint8_t { Snapped, Mono, Billboard };
-
class Handler
{
public:
- // Line-number hitboxes for shooting numbers (was Wall::Editor::NumberHit).
- // The top bit distinguishes a number hit from a regular token index when a
- // single id space is shared with the projectile system.
- static constexpr size_t HIT_BASE = size_t(1) << 32;
+ static constexpr
+ size_t HIT_BASE = size_t(1) << 32;
static bool is_NumberHit(size_t idx) { return idx >= HIT_BASE; }
static size_t line_Of (size_t idx) { return idx - HIT_BASE; }
struct NumberHit { size_t index; Vec3 aabb_Min; Vec3 aabb_Max; };
- struct MonoMetrics { // fed to the vim cursor renderer
+ struct MonoMetrics {
double char_w;
double he_y;
- double left_z; // z of column 0's left edge
+ double left_z;
double row0_y;
};
@@ -62,10 +38,9 @@ public:
Handler() = default;
Handler(Wall::Role role, float world_scale, LayoutMode mode);
- // ── configuration / render options ────────────────────────────────────
- void set_Layout (LayoutMode m); // switch + reposition (no full rebuild)
+ void set_Layout (LayoutMode m);
void set_FaceMode (FaceMode f) { _face = f; }
- void set_Lexing (bool on) { _lex_enabled = on; } // info/compiler/program off
+ void set_Lexing (bool on) { _lex_enabled = on; }
void set_ShowNumbers (bool on, bool both_columns = false);
void set_Bordered (bool on) { _bordered = on; }
void set_Underline (bool on) { _underline = on; }
@@ -76,85 +51,61 @@ public:
// ── content ────────────────────────────────────────────────────────────
void clear();
- void set_Lines (const DynArray<std::string>& lines); // (re)build from text; relayouts + marks dirty
- void append_Line(const std::string& text); // program output: scroll up, add at bottom
- void set_AppendBaseline(double y) { _append_y = y; } // bottom row y for append_Line (program wall)
- void set_AppendColor (SDL_FColor c) { _append_color = c; } // output text color for append_Line
-
- // ── ownership transfer (the heart of the rewrite) ─────────────────────
- // detach: remove a token from its line, hand ownership out (pick / shoot-off).
- // empties + drops the line if it was the last token. Marks dirty.
- // attach: take ownership of a token and place it.
- // Snapped -> resolve_Target + place_OnLine (fuses today's on_LinePlaced +
- // Wall::Text::from_Drop).
- // Mono -> SHOOT-MERGE. impact maps to (row,col) via impact_To_RowCol;
- // col snaps to the nearest inter-token gap (never bisects a
- // word); the token's text is spliced into the edit buffer
- // (VimBuffer::insert_Word_At) so it survives the next keystroke.
- // The incoming mesh is dropped; re-projection rebuilds it.
- TokenPtr detach(Token* t);
- void attach(DynArray<TokenPtr> tokens, const Vec3& impact); // a shot run, placed as one block
- void clear_ToFloor(DynArray<TokenPtr>& out); // was Wall::Text::clear_ToFloor
-
- // ── async lexing (pull model; GameState owns the CppHighlightSystem) ──
- // Loop: if (h.is_Dirty()) hl.request(h.render_Source(), h.generation());
- // if (hl.poll(spans, gen)) h.apply_Highlight(spans, gen);
- // render_Source assigns byte_begin/byte_end across all tokens (was
- // Text::View::render_Source); apply_Highlight is the best-overlap span match
- // (was Text::View::apply_Spans) and ignores stale generations.
+ void set_Lines (const DynArray<std::string>& lines);
+ void append_Line(const std::string& text);
+ void set_AppendBaseline(double y) { _append_y = y; }
+ void set_AppendColor (SDL_FColor c) { _append_color = c; }
+
+ // ── ownership transfer ────────────────────────────────────────────────
+ TokenPtr detach (Token::Data* t);
+ void attach (DynArray<TokenPtr> tokens, const Vec3& impact);
+ void clear_ToFloor(DynArray<TokenPtr>& out);
+
+ // ── async lexing ──────────────────────────────────────────────────────
bool is_Dirty () const { return _lex_enabled && _dirty; }
uint64_t generation () const { return _gen; }
std::string render_Source();
void apply_Highlight(const DynArray<HiSpan>& spans, uint64_t gen);
void mark_Dirty ();
- // ── queries (for PickSystem + projectile landing) ─────────────────────
- // PickSystem walks get_Lines() across all Handlers, tracking the best
- // {Handler*, Token*}; PickResult carries those instead of a wall index.
- const DynArray<Line>& get_Lines() const { return _lines; }
- Token* token_At_Byte (size_t off);
- DynArray<std::string> as_Lines() const; // for vim load + file sync
+ // ── queries ───────────────────────────────────────────────────────────
+ Token::Data* token_At_Byte(size_t off);
+ DynArray<std::string> as_Lines () const;
+ const DynArray<Line>& get_Lines () const { return _lines; }
// ── per-frame ──────────────────────────────────────────────────────────
- void update(double dt, const Camera& cam); // bounce springs, tilt-to-eye, number placement
- void render(GlRenderer& gl, const Camera& cam, const Light& light) const;
- void scroll(double delta); // shift every row + its tokens
+ void update (double dt, const Camera& cam);
+ void render (GlRenderer& gl, const Camera& cam, const Light& light) const;
+ void scroll (double delta);
- // ── line-number shooting (Snapped/editor) ─────────────────────────────
- DynArray<NumberHit> number_Hitboxes () const;
- double scroll_DeltaToEye (size_t line_id, double eye_y) const;
- void bounce (size_t line_id);
+ // ── line-number ───────────────────────────────────────────────────────
+ double scroll_DeltaToEye (size_t line_id, double eye_y) const;
+ void bounce (size_t line_id);
+ DynArray<NumberHit>
+ number_Hitboxes () const;
// ── vim cursor support (Mono) ──────────────────────────────────────────
MonoMetrics mono_Metrics() const;
double row_Y (int row) const;
- // ── live editing (Mono) — Handler owns the VimBuffer ──────────────────
- // begin_Edit builds the buffer from the current lines and switches to Mono;
- // Wall::Connection becomes a thin overlay that forwards keys via feed_Keys
- // and reads cursor_Row/Col/Mode for the cursor box + cmdline. The Write /
- // WriteQuit / Exit / Discard request that GameState acts on (file sync, end
- // connection) surfaces from feed_Keys.
+ // ── live editing (Mono) ───────────────────────────────────────────────
enum class EditReq : uint8_t { None, Write, WriteQuit, Exit, Discard };
void begin_Edit (double eye_y);
void end_Edit ();
bool edit_Active() const { return _layout == LayoutMode::Mono && _vim.get() != nullptr; }
- EditReq feed_Keys (DynArray<int>& keys, double eye_y); // drives vim, re-projects, re-lexes
+ EditReq feed_Keys (DynArray<int>& keys, double eye_y);
void mark_Saved ();
- int cursor_Row () const;
- int cursor_Col () const;
- VimBuffer::Mode cursor_Mode () const;
- const std::string& cmdline_Text () const;
- const DynArray<std::string>& status_Lines () const;
- std::string modeline () const; // mode/cmdline/status text for the overlay
-
- // ── billboard label (LayoutMode::Billboard) ───────────────────────────
- // One centred line of words that faces the eye (or a fixed yaw) and jiggles
- // on projectile kicks. Mirrors the old TextLabel API verbatim so call sites
- // swap a TextLabel for a Handler with no other change. detach() (no args) is
- // the anchor-detach; detach(Token*) above is the ownership-transfer one.
+ int cursor_Row () const;
+ int cursor_Col () const;
+ const std::string& cmdline_Text() const;
+ std::string modeline () const;
+ VimBuffer::Mode cursor_Mode () const;
+ const DynArray<std::string>&
+ status_Lines() const;
+
+ // ── billboard label ───────────────────────────────────────────────────
void set_Text (const std::string& text);
void set_Text (const std::string& text, SDL_FColor color, float depth, float scale);
void set_Centre (const Vec3& p);
@@ -166,26 +117,26 @@ public:
void set_Collidable(bool on);
void kick (size_t word_index, const Vec3& projectile_vel, const Vec3& normal);
bool is_Empty () const { return _lines.empty() || _lines[0].tokens.empty(); }
- const DynArray<TokenPtr>& get_Words() const;
+ const DynArray<TokenPtr>&
+ get_Words () const;
void draw (GlRenderer& gl, const Camera& cam, const Light& light) const { render(gl, cam, light); }
private:
- DynArray<Line> _lines; // single source of truth
+ DynArray<Line> _lines;
Wall::Role _role { Wall::Role::Editor };
- Wall::Transform _tf {}; // cached world placement (pos + yaw)
+ Wall::Transform _tf {};
LayoutMode _layout { LayoutMode::Snapped };
FaceMode _face { FaceMode::Pitch };
- // style defaults (per-line may override)
float _depth { Token::depth_wall };
float _scale { 0.003f };
double _line_height { 0.0 };
double _word_spacing { 0.0 };
- double _append_y { 0.0 }; // program-wall bottom row
- SDL_FColor _append_color { 0.45f, 0.45f, 0.45f, 1.0f }; // append_Line text color
+ double _append_y { 0.0 };
+ SDL_FColor _append_color { 0.45f, 0.45f, 0.45f, 1.0f };
- DynArray<std::string> _status; // multi-line status (vim :help etc.), for the overlay
+ DynArray<std::string> _status;
bool _lex_enabled { true };
bool _show_numbers { false };
@@ -205,40 +156,44 @@ private:
double _row0_y { 0.0 };
double _eye_y { 0.0 };
- // Edit model — present only while editing in Mono mode (the editor Handler).
cpputils::unique_ptr<VimBuffer> _vim;
unsigned _vim_seen_version { 0 };
- // ── billboard-label state (LayoutMode::Billboard) ─────────────────────
enum class PositionMode : uint8_t { Absolute, Anchor };
- PositionMode _pos_mode { PositionMode::Absolute };
- const Vec3* _anchor_ptr { nullptr };
- Vec3 _centre {};
+ const Vec3* _anchor_ptr { nullptr };
+ bool _billboard { true };
+ bool _collidable { true };
+ float _face_yaw { 0.0f };
Vec3 _attach_offset {};
- SDL_FColor _style_color { 1.0f, 1.0f, 0.0f, 1.0f };
- std::string _label_text;
- DynArray<double> _label_off; // per-word centre offset along the row
- bool _billboard { true };
- bool _collidable { true };
- float _face_yaw { 0.0f };
-
- void relayout_Billboard(); // (re)build the single centred line
+ Vec3 _centre {};
+ SDL_FColor _style_color { 1.0f, 1.0f, 0.0f, 1.0f };
+ std::string _label_text;
+ PositionMode _pos_mode { PositionMode::Absolute };
+ DynArray<double> _label_off;
+
+ void relayout_Billboard();
void billboard_Update (double dt, const Camera& cam);
Vec3 label_Centre () const;
// layout
- void relayout (); // dispatch on _layout
+ void relayout ();
void relayout_Snapped();
void relayout_Mono ();
- // Snapped placement internals (lifted from Wall::Editor)
- struct Target { enum Kind { Merge, Insert } kind; size_t line_index; double new_y; double push_dist; };
- Target resolve_Target (double y) const;
- Line& apply_Target (const Target& t);
- void attach_Snapped (DynArray<TokenPtr>& block, const Vec3& impact);
- void attach_Mono (DynArray<TokenPtr>& block, const Vec3& impact);
- void maintain_Numbers(double dt, const Camera& cam); // Snapped: rank + bounce + place
- void apply_Tilt (const Camera& cam); // FaceMode pitch toward eye
- // Mono shoot-merge: world impact -> buffer (row,col), col snapped to a gap.
+
+ // Snapped placement internals
+ struct Target {
+ enum Kind { Merge, Insert } kind;
+ size_t line_index;
+ double new_y;
+ double push_dist;
+ };
+
+ Target resolve_Target (double y) const;
+ Line& apply_Target (const Target& t);
+ void attach_Snapped (DynArray<TokenPtr>& block, const Vec3& impact);
+ void attach_Mono (DynArray<TokenPtr>& block, const Vec3& impact);
+ void maintain_Numbers(double dt, const Camera& cam);
+ void apply_Tilt (const Camera& cam);
bool impact_To_RowCol(const Vec3& impact, int& row, int& col) const;
};
diff --git a/src/text/line.cpp b/src/text/line.cpp
index b7e8edb..a6252b4 100644
--- a/src/text/line.cpp
+++ b/src/text/line.cpp
@@ -16,7 +16,7 @@ Line::width(double gap) const
return w;
}
-Token*
+Token::Data*
Line::token_At_Byte(size_t off) const
{
for (const TokenPtr& t : tokens)
diff --git a/src/text/line.hpp b/src/text/line.hpp
index 8662e90..0f90ddb 100644
--- a/src/text/line.hpp
+++ b/src/text/line.hpp
@@ -4,8 +4,8 @@
//
// Text::Line is the single owning line structure. It replaces three things that
// each modeled "a row of tokens" for one context:
-// - Wall::Editor::Line (non-owning Text3d*, line numbers, bounce)
-// - Text::Line/Token (logical row for tree-sitter, non-owning view ptr)
+// - Wall::Editor::Line (non-owning Text::Token::Data*, line numbers, bounce)
+// - Text::Line/Token::Data (logical row for tree-sitter, non-owning view ptr)
// - EditorRow (vim mode's parallel owned meshes)
//
// A Line OWNS its tokens. Layout that needs cross-line context (snapping,
@@ -20,36 +20,5 @@
namespace Text
{
-struct Line
-{
- DynArray<TokenPtr> tokens; // OWNS the row's word meshes, visual order
-
- double y { 0.0 }; // world-space row height (shared by tokens)
- double line_height { 0.0 }; // spacing to the next row (0 = use Handler default)
- double word_spacing { 0.0 }; // gap between tokens (0 = use Handler default)
-
- // Line numbering. The Handler decides numbering policy (one vs two columns,
- // visibility); the meshes + per-row bounce spring live here because the
- // bounce is driven per-line when a number is shot (Wall::Editor::bounce).
- int number { -1 }; // 1-based; -1 = unassigned
- bool show_number { false };
- double bounce_off { 0.0 };
- double bounce_vel { 0.0 };
- TokenPtr num_left; // left gutter digit (always, when shown)
- TokenPtr num_right; // right column digit (editor's both_columns only)
-
- bool is_Empty() const { return tokens.empty(); }
- size_t count () const { return tokens.size(); }
-
- // Total visual width = sum of glyph widths + internal gaps. Used by Handler
- // layout (block centering, snap). gap defaults to the line/Handler spacing.
- double width(double gap) const;
-
- // Find the owned token covering a byte offset in the Handler's rendered
- // source (byte_begin/byte_end are assigned by Handler::render_Source).
- Token* token_At_Byte(size_t off) const;
-
- void clear(); // drops tokens + number meshes, resets bounce
-};
} // namespace Text
diff --git a/src/text/text.cpp b/src/text/text.cpp
index e69de29..696bfb1 100644
--- a/src/text/text.cpp
+++ b/src/text/text.cpp
@@ -0,0 +1,91 @@
+#include "text.hpp"
+
+
+namespace Text
+{
+
+DynArray<std::string>
+wrap_String(const std::string& text, size_t max_chars)
+{
+ std::string word;
+ std::string current_line;
+
+ DynArray<std::string> lines;
+
+ auto flush_line = [&]() {
+ while (!current_line.empty() && current_line.back() == ' ')
+ current_line.pop_back();
+ lines.push_back(current_line);
+ current_line.clear();
+ };
+
+ auto flush_word = [&]() {
+ if (word.empty()) return;
+ if (current_line.size() + word.size() + (current_line.empty() ? 0 : 1)
+ > max_chars) {
+ flush_line();
+ }
+ if (!current_line.empty()) current_line += ' ';
+ current_line += word;
+ word.clear();
+ };
+
+ for (char c : text) {
+ if (c == '\n') {
+ flush_word();
+ flush_line();
+ }
+ else if (c == ' ' || c == '\t') {
+ flush_word();
+ }
+ else {
+ word += c;
+ }
+ }
+ flush_word();
+ flush_line();
+
+ return lines;
+}
+
+std::string
+ascii_Sanitize(const std::string& in)
+{
+ std::string out;
+ out.reserve(in.size());
+
+ size_t i = 0;
+ while (i < in.size()) {
+ const unsigned char c = static_cast<unsigned char>(in[i]);
+
+ if (c < 0x80) {
+ out += static_cast<char>(c);
+ ++i;
+ continue;
+ }
+
+ // U+2000–U+20FF punctuation block: E2 80 xx
+ if (c == 0xE2 && i + 2 < in.size() &&
+ static_cast<unsigned char>(in[i + 1]) == 0x80) {
+ const unsigned char c3 = static_cast<unsigned char>(in[i + 2]);
+ switch (c3) {
+ case 0x98: case 0x99: out += '\''; break; // ‘ ’
+ case 0x9C: case 0x9D: out += '"'; break; // “ ”
+ case 0x93: case 0x94: out += '-'; break; // – —
+ default: out += '\''; break;
+ }
+ i += 3;
+ continue;
+ }
+
+ // Any other multibyte sequence: one '?', skip continuation bytes.
+ out += '?';
+ ++i;
+ while (i < in.size() &&
+ (static_cast<unsigned char>(in[i]) & 0xC0) == 0x80)
+ ++i;
+ }
+ return out;
+}
+
+} // namespace Text
diff --git a/src/text/text.hpp b/src/text/text.hpp
index e69de29..13b0103 100644
--- a/src/text/text.hpp
+++ b/src/text/text.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+#include <string>
+
+#include "utils/globals.hpp"
+
+
+namespace
+Text
+{
+ class Handler;
+ struct Line;
+ struct HiSpan;
+
+ namespace Token {
+ struct Ai;
+ struct Data;
+ struct Desc;
+ enum class Type : uint8_t;
+ }
+
+
+ enum class ColorSource : uint8_t { Lexed, Lint, Static };
+ enum class FaceMode : uint8_t { None, Yaw, Pitch, Both };
+ enum class LayoutMode : uint8_t { Snapped, Mono, Billboard };
+
+
+ DynArray<std::string>
+ wrap_String(const std::string& text, size_t max_chars);
+
+ std::string
+ ascii_Sanitize(const std::string& in);
+
+} // namespace Text
+
+
+namespace
+Text
+{
+
+ struct HiSpan
+ {
+ size_t byte_begin;
+ size_t byte_end;
+ Text::Token::Type type;
+ };
+
+
+ namespace CppHighlight
+ {
+ DynArray<HiSpan> classify (const std::string& source);
+ Text::Token::Type classify_Word(const std::string& word);
+ }
+
+} // namespace Text
+
+
+namespace
+Text
+{
+
+ struct Line
+ {
+ DynArray<TokenPtr> tokens;
+
+ 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;
+
+ bool is_Empty() const { return tokens.empty(); }
+ size_t count () const { return tokens.size(); }
+
+ double width(double gap) const;
+
+ Token::Data* token_At_Byte(size_t off) const;
+
+ void clear();
+ };
+
+
+} // namespace Text
diff --git a/src/text_3d/text_mesh.cpp b/src/text/text_mesh.cpp
similarity index 100%
rename from src/text_3d/text_mesh.cpp
rename to src/text/text_mesh.cpp
diff --git a/src/text_3d/text_mesh.hpp b/src/text/text_mesh.hpp
similarity index 100%
rename from src/text_3d/text_mesh.hpp
rename to src/text/text_mesh.hpp
diff --git a/src/text/token.cpp b/src/text/token.cpp
index 65e383e..6cb6946 100644
--- a/src/text/token.cpp
+++ b/src/text/token.cpp
@@ -1,25 +1,15 @@
-// text/token.cpp
-//
-// Port of text_3d.cpp onto Text::Token. Mesh build, GPU caching, carry-follow
-// update, and the AI sparkle tail are carried over unchanged. The only behavior
-// changes are in color routing (ColorSource instead of the set_Look sentinel)
-// and AI ownership (optional component, constructed by enable_Ai()).
-
#include <SDL3/SDL.h>
-#include "cpp_token/token_color.hpp"
#include "entities/camera.hpp"
#include "entities/world.hpp"
#include "rendering/gl_renderer.hpp"
-#include "systems/cpp_highlight_system.hpp" // CppHighlight::classify_Word
+#include "systems/cpp_highlight_system.hpp"
#include "text/token.hpp"
#include "utils/globals.hpp"
namespace Text
{
-// ── sparkle-tail helpers (file-local; unchanged from text_3d.cpp) ─────────────
-
static SDL_FColor
aiGlintColor(Token::Ai::State s)
{
@@ -38,7 +28,7 @@ static Vec3 v_norm(const Vec3& v)
return (l < 1e-9) ? Vec3{0,0,1} : Vec3{v.x/l, v.y/l, v.z/l};
}
-// Camera-facing filled disc (triangle fan). Same vertex layout as the text mesh.
+// Camera-facing filled disc (triangle fan)
static void
append_disc(DynArray<float>& v, DynArray<int>& idx,
const Vec3& c, const Vec3& right, const Vec3& up,
@@ -85,9 +75,10 @@ static SDL_FColor mix_col(SDL_FColor a, SDL_FColor b, float k)
}
-// ── construction / lifetime ───────────────────────────────────────────────────
-
-Token::Token(const std::string& txt,
+namespace
+Token
+{
+Data::Data(const std::string& txt,
float dpth, float scle,
Vec3 position, SDL_FColor colr)
: GameObject(position, ObjectType::Text,
@@ -95,23 +86,20 @@ Token::Token(const std::string& txt,
true, true, true),
text(txt), color(colr), depth(dpth), scale(scle)
{
- // Lone-token colour (Lint). Editor tokens get switched to Lexed by the Handler.
- set_Color(TokenColor::get(CppHighlight::classify_Word(text)));
+ set_Color(Text::Token::get_Color(CppHighlight::classify_Word(text)));
rebuild_Mesh();
body.hover_phase = Random::unit() * SDL_PI_D * 2.0;
- // AI personality (by value, like the old Text3d ctor).
ai.aggressiveness = Random::unit();
ai.wiggle_phase = Random::unit() * SDL_PI_D * 2.0;
- ai.wiggle_freq = 3.0 + Random::unit() * 4.0; // 3–7 rad/s
+ ai.wiggle_freq = 3.0 + Random::unit() * 4.0;
ai.speed = 2.5 + Random::unit() * 2.5;
}
-Token::~Token()
+Data::~Data()
{
- // Renderer owns the GL context; only delete if buffers were created.
if (gpu_vbo || gpu_ebo)
GlRenderer::free_MeshBuffers(gpu_vbo, gpu_ebo);
if (gpu_vbo_sparkle || gpu_ebo_sparkle)
@@ -119,46 +107,40 @@ Token::~Token()
}
-// ── color ────────────────────────────────────────────────────────────────────
-
void
-Token::set_Color(SDL_FColor c)
+Data::set_Color(SDL_FColor c)
{
- color = c; // direct; color_src unchanged (old semantics)
+ color = c;
}
void
-Token::set_Look(SDL_FColor c, float new_depth)
+Data::set_Look(SDL_FColor c, float new_depth)
{
- // Legacy shim. ConfigColor::keep == {0,-1,0,1} hits the g==-1 "keep" branch.
- if (c.r == -1.0f) color = TokenColor::get(CppHighlight::classify_Word(text));
+ if (c.r == -1.0f) color = Text::Token::get_Color(CppHighlight::classify_Word(text));
else if (c.g == -1.0f) { /* keep current colour */ }
else color = c;
set_Depth(new_depth);
}
void
-Token::recolor()
+Data::recolor()
{
- // Lexed → coloured by Handler::apply_Highlight; Static → pinned; Lint → reclassify.
if (color_src == ColorSource::Lint)
- color = TokenColor::get(CppHighlight::classify_Word(text));
+ color = Text::Token::get_Color(CppHighlight::classify_Word(text));
}
void
-Token::set_Depth(float new_depth)
+Data::set_Depth(float new_depth)
{
if (new_depth == depth) return;
depth = new_depth;
- rebuild_Mesh(); // never touches color
+ rebuild_Mesh();
}
-// ── misc ───────────────────────────────────────────────────────────────────────
-
Vec3
-Token::words_centroid(const DynArray<TokenPtr>& words)
+Data::words_centroid(const DynArray<TokenPtr>& words)
{
Vec3 c{};
double n = 0.0;
@@ -173,19 +155,19 @@ Token::words_centroid(const DynArray<TokenPtr>& words)
Vec3
-Token::get_Distance(Vec3 position) const
+Data::get_Distance(Vec3 position) const
{
return pos - position;
}
void
-Token::rebuild_Mesh()
+Data::rebuild_Mesh()
{
loaded = mesh.build(font_path, text, 64, depth, scale);
if (!loaded) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
- "Text::Token: mesh build failed for \"%s\"", text.c_str());
+ "Text::Token::Data: mesh build failed for \"%s\"", text.c_str());
return;
}
@@ -208,8 +190,7 @@ Token::rebuild_Mesh()
static_cast<double>(depth) * 0.5
};
- // Pack the GPU-ready interleaved buffer once. draw() just uploads/binds it.
- packed_verts.clear();
+ packed_verts. clear();
packed_indices.clear();
const size_t nv = mesh.verts.size();
packed_verts.reserve(nv * 9);
@@ -227,12 +208,12 @@ Token::rebuild_Mesh()
packed_indices.push_back(t.b);
packed_indices.push_back(t.c);
}
- gpu_dirty = true; // mesh changed → re-upload on next draw
+ gpu_dirty = true;
}
void
-Token::update(const Vec3& camera_pos, const double view_mat[9], float carry_dist)
+Data::update(const Vec3& camera_pos, const double view_mat[9], float carry_dist)
{
float fx = -static_cast<float>(view_mat[6]);
float fy = -static_cast<float>(view_mat[7]);
@@ -244,10 +225,8 @@ Token::update(const Vec3& camera_pos, const double view_mat[9], float carry_dist
}
-// ── draw (word mesh + AI sparkle tail) ─────────────────────────────────────────
-
void
-Token::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
+Data::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
{
if (!loaded || packed_indices.empty()) return;
@@ -267,7 +246,6 @@ Token::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
else
gl.set_Emissive(0.0f, 0.0f, 0.0f, 0.0f);
- // Draw the actual word FIRST, while MVP/normal/face-shade are still correct.
gl.draw_MeshCached(gpu_vbo, gpu_ebo, gpu_index_count, gpu_dirty,
packed_verts.data(), (int)packed_verts.size() / 9,
packed_indices.data(), (int)packed_indices.size());
@@ -276,7 +254,7 @@ Token::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
gl.set_Emissive (0.0f, 0.0f, 0.0f, 0.0f);
gl.set_FaceShade(0.0f, 0.0f, 0.0f, 1.0f, false);
- // ── AI sparkle tail (only on active AI state) ─────────────────────────
+ // ── AI sparkle tail ───────────────────────────────────────────────────
if (ai.state == Ai::State::Idle) return;
const Uint64 now = SDL_GetTicks();
@@ -374,4 +352,8 @@ Token::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
gl.set_FaceShade(0.0f, 0.0f, 0.0f, 1.0f, false);
}
+
+} // namespace Token
+
+
} // namespace Text
diff --git a/src/text/token.hpp b/src/text/token.hpp
index d45401c..26088e7 100644
--- a/src/text/token.hpp
+++ b/src/text/token.hpp
@@ -1,72 +1,98 @@
#pragma once
-//
-// text/token.hpp — Phase 1 contract
-//
-// Text::Token is the renamed, cleaned-up successor to Text3d. It is still the
-// atomic unit: ONE whitespace-delimited word == ONE extruded mesh (TextMesh
-// rasterizes the whole string in one pass, so this granularity is fixed by the
-// mesh layer). It remains a GameObject so physics / pick / projectile code keep
-// working unchanged.
-//
-// Three things change vs. Text3d:
-// 1. Color provenance is explicit (ColorSource) instead of the {0,-1,0}
-// sentinel decoded inside set_Look.
-// 2. byte_begin/byte_end are first-class (were only in Text::View) so a
-// Handler can map tree-sitter spans straight onto the token.
-// 3. The AI state is an OPTIONAL owned component. Free/floor words own one;
-// document tokens (editor/program/info) carry nullptr and stay lean.
-//
#include <SDL3/SDL.h>
#include <cstdint>
#include <string>
-#include "entities/game_object.hpp" // GameObject, RigidBody, CollisionShape, ObjectType, PhysicsMode
-#include "text_3d/text_mesh.hpp" // TextMesh
-#include "utils/config.hpp" // ConfigColor (defaults)
-#include "utils/globals.hpp" // Vec3, DynArray, cpputils::unique_ptr
+#include "entities/game_object.hpp"
+#include "text/text.hpp"
+#include "text/text_mesh.hpp"
+#include "utils/config.hpp"
+#include "utils/globals.hpp"
#include "utils/light.hpp"
class Camera;
class GlRenderer;
-namespace Text
+namespace
+Text
{
-// Where a token's color comes from. Replaces the magic-float ConfigColor::keep.
-// Lexed — colored by the owning Handler's tree-sitter spans (apply_Highlight).
-// Lint — self-classified as a lone token: classify_Word(text). Free words on
-// the floor, and AI words returning to Idle, use this.
-// Static — a fixed color set explicitly and never recomputed: line numbers,
-// program output (dark_gray), AI state tints (attack/flee/stunned),
-// info/compiler text.
-enum class ColorSource : uint8_t { Lexed, Lint, Static };
-
-// Rotate-to-eye policy. Stored on the Handler (document-wide) and applied to its
-// tokens each frame; kept here so Token and Handler share the vocabulary.
-// None — fixed yaw/pitch (free floor words).
-// Yaw — follow the eye horizontally (carried words).
-// Pitch — tilt up/down toward the eye, clamped (wall text: tilt_TowardEye).
-// Both — billboard fully (labels).
-enum class FaceMode : uint8_t { None, Yaw, Pitch, Both };
-
-
-struct Token final : public GameObject
+
+namespace
+Token
+{
+ struct Ai;
+ struct Data;
+ struct Desc;
+
+ enum class Type : uint8_t
+ {
+ Delimiter,
+ Identifier,
+ Keyword,
+ Literal,
+ Operator,
+ Preprocessor,
+ Type,
+ Function,
+ Variable,
+ Comment,
+ StringLit,
+ NumberLit,
+ Namespace,
+ };
+
+ // token_db.cpp START
+ inline const std::string font_path =
+ "/usr/share/fonts/dejavu/DejaVuSansMono.ttf";
+
+ inline constexpr float depth_wall = 0.06f;
+ inline constexpr float depth_floor = 0.14f;
+ inline constexpr float depth_compiler = 0.02f;
+
+ const char* get_TypeName (Type type);
+ SDL_FColor get_Color (Type t);
+ Desc get_Description(const std::string& text, Type type);
+ DynArray<std::string>
+ get_TestArrayAllKeywords();
+ // token_db.cpp END
+
+} // namespace Token::Data
+
+
+
+template<typename... Args>
+TokenPtr make_Token(Args&&... args)
+{
+ return cpputils::make_unique<Token::Data>(std::forward<Args>(args)...);
+}
+
+} // namespace Text
+
+
+
+namespace
+Text
{
- // ── shared constants (unchanged from Text3d) ──────────────────────────
- static inline const std::string font_path =
- "/usr/share/fonts/dejavu/DejaVuSansMono.ttf";
- static inline constexpr float depth_wall = 0.06f;
- static inline constexpr float depth_floor = 0.14f;
- static inline constexpr float depth_compiler = 0.02f;
+namespace
+Token
+{
+
+ struct Desc
+ {
+ std::string short_desc;
+ std::string long_desc;
+ };
- // ── AI component (was Text3dAI; now owned only by free words) ──────────
struct Ai
{
enum class State : uint8_t { Idle, Attack, Flee, Stunned };
+ bool in_pause = false;
+ bool has_stolen = false;
State state = State::Idle;
double state_timer = 0.0;
double aggressiveness = 0.0;
@@ -75,110 +101,87 @@ struct Token final : public GameObject
double wiggle_freq = 4.0;
double speed = 2.0;
- bool in_pause = false;
double hover_height_saved = 0.0;
double orbit_angle = 0.0;
double orbit_dir = 1.0;
- bool has_stolen = false;
- DynArray<cpputils::unique_ptr<Token>> stolen; // carried-off loot
+ DynArray<cpputils::unique_ptr<Data>> stolen;
Vec3 trail_dir { 1.0, 0.0, 0.0 };
};
+ struct Data final : public GameObject
+ {
+ std::string text;
+ SDL_FColor color;
+ TextMesh mesh;
- // ── text / appearance ─────────────────────────────────────────────────
- std::string text;
- SDL_FColor color;
- TextMesh mesh;
-
- ColorSource color_src { ColorSource::Lint };
+ ColorSource color_src { ColorSource::Lint };
- float depth;
- float scale;
- float half_extent { 0.0f }; // width half-extent (along the glyph row)
- float half_extent_y { 0.2f }; // height half-extent
- float pitch { 0.0f };
+ float depth;
+ float scale;
+ float half_extent { 0.0f };
+ float half_extent_y { 0.2f };
+ float pitch { 0.0f };
- bool loaded { false };
- bool front_tint { true };
- bool glow_enabled { false };
- float glow_strength { 0.2f };
- float back_dim { 0.45f };
- Vec3 ribbon_local {};
+ bool loaded { false };
+ bool front_tint { true };
+ bool glow_enabled { false };
+ float glow_strength { 0.2f };
+ float back_dim { 0.45f };
+ Vec3 ribbon_local {};
- // ── byte span (valid only while owned by a Handler; set by render_Source)
- size_t byte_begin { 0 };
- size_t byte_end { 0 };
+ size_t byte_begin { 0 };
+ size_t byte_end { 0 };
- // ── billboard-label jiggle (only used in LayoutMode::Billboard; was
- // Text::Handler's parallel _jiggle_off/_jiggle_vel arrays) ──
- Vec3 jiggle_off {};
- Vec3 jiggle_vel {};
+ Vec3 jiggle_off {};
+ Vec3 jiggle_vel {};
- // ── AI state (by-value; idle on document tokens, animated only on free
- // words by WordAiSystem — matches the old Text3d::ai member exactly) ──
- Ai ai;
+ Ai ai;
- // ── GPU caches (mutable; identical to Text3d) ─────────────────────────
- mutable bool gpu_dirty { true };
- mutable int gpu_index_count { 0 };
- mutable unsigned int gpu_vbo { 0 };
- mutable unsigned int gpu_ebo { 0 };
- mutable unsigned int gpu_vbo_sparkle { 0 };
- mutable unsigned int gpu_ebo_sparkle { 0 };
- mutable int gpu_sparkle_index_count { 0 };
+ mutable bool gpu_dirty { true };
+ mutable int gpu_index_count { 0 };
+ mutable unsigned int gpu_vbo { 0 };
+ mutable unsigned int gpu_ebo { 0 };
+ mutable unsigned int gpu_vbo_sparkle { 0 };
+ mutable unsigned int gpu_ebo_sparkle { 0 };
+ mutable int gpu_sparkle_index_count { 0 };
- static constexpr int SPARKLE_TRAIL = 14;
- mutable int sparkle_trail_count { 0 };
- mutable Uint64 sparkle_last_ms { 0 };
- mutable Vec3 sparkle_trail[SPARKLE_TRAIL];
+ static constexpr int SPARKLE_TRAIL = 14;
+ mutable int sparkle_trail_count { 0 };
+ mutable Uint64 sparkle_last_ms { 0 };
+ mutable Vec3 sparkle_trail[SPARKLE_TRAIL];
- DynArray<int> packed_indices;
- DynArray<float> packed_verts;
+ DynArray<int> packed_indices;
+ DynArray<float> packed_verts;
- // ── lifetime ───────────────────────────────────────────────────────────
- Token(const std::string& txt,
- float depth, float scale,
- Vec3 position,
- SDL_FColor colr = ConfigColor::blue);
- ~Token() override;
+ Data (const std::string& txt,
+ float depth, float scale,
+ Vec3 position,
+ SDL_FColor colr = ConfigColor::blue);
+ ~Data() override;
- // ── colour ────────────────────────────────────────────────────────────
- // set_Color — set the colour directly (does NOT change color_src; matches
- // old Text3d::set_Color, so existing call sites keep working).
- // set_Look — legacy shim: ConfigColor::keep keeps colour, an r==-1
- // sentinel reclassifies, otherwise sets colour; then depth.
- // recolor — Lint: reclassify from text. Lexed/Static: no-op.
- // color_src is only consulted by Handler::apply_Highlight (which recolours
- // its Lexed tokens) and recolor(); free/floor tokens ignore it.
- void set_Color (SDL_FColor c);
- void set_Look (SDL_FColor c, float new_depth);
- void set_ColorSource(ColorSource src) { color_src = src; }
- void recolor ();
+ void set_Color (SDL_FColor c);
+ void set_Look (SDL_FColor c, float new_depth);
+ void set_ColorSource(ColorSource src) { color_src = src; }
+ void recolor ();
- void set_Depth (float depth); // rebuilds mesh only if depth changed; never touches colour
- void rebuild_Mesh();
+ void set_Depth (float depth);
+ void rebuild_Mesh();
- // ── misc (unchanged) ───────────────────────────────────────────────────
- static Vec3 words_centroid(const DynArray<cpputils::unique_ptr<Token>>& words);
- Vec3 get_Distance(Vec3 position) const;
- void update (const Vec3& camera_pos, const double view_mat[9], float carry_dist = 1.5f);
- void draw (GlRenderer& gl, const Camera& camera, const Light& light) const override;
-};
+ static Vec3 words_centroid(const DynArray<cpputils::unique_ptr<Data>>& words);
+ Vec3 get_Distance(Vec3 position) const;
+ void update (const Vec3& camera_pos, const double view_mat[9], float carry_dist = 1.5f);
+ void draw (GlRenderer& gl, const Camera& camera, const Light& light) const override;
+ };
-using TokenPtr = cpputils::unique_ptr<Token>;
+} // namespace Token::Data
-template<typename... Args>
-TokenPtr make_Token(Args&&... args)
-{
- return cpputils::make_unique<Token>(std::forward<Args>(args)...);
-}
} // namespace Text
diff --git a/src/cpp_token/token_db.cpp b/src/text/token_db.cpp
similarity index 71%
rename from src/cpp_token/token_db.cpp
rename to src/text/token_db.cpp
index e4894d0..9b95079 100644
--- a/src/cpp_token/token_db.cpp
+++ b/src/text/token_db.cpp
@@ -1,7 +1,9 @@
-#include "token_db.hpp"
#include <unordered_map>
-namespace {
+#include "token.hpp"
+
+namespace
+{
struct Entry { const char* short_desc; const char* long_desc; };
@@ -92,34 +94,63 @@ table()
return m;
}
-// Generic fallback description per token type, for tokens not in the table
-// (e.g. user-named variables, arbitrary literals).
-TokenDB::Desc
-generic_For(TokenType type)
+
+} // namespace
+
+
+namespace
+Text
+{
+namespace
+Token
+{
+
+SDL_FColor
+get_Color(Type t)
+{
+ switch (t) {
+ case Type::Keyword: return ConfigColor::orange;
+ case Type::Type: return ConfigColor::green;
+ case Type::Identifier: return ConfigColor::light_gray;
+ case Type::Literal: return ConfigColor::red;
+ case Type::Operator: return ConfigColor::soft_blue;
+ case Type::Delimiter: return ConfigColor::slate_blue;
+ case Type::Preprocessor:return ConfigColor::pink;
+ case Type::Function: return ConfigColor::blue;
+ case Type::Variable: return ConfigColor::pale_sky;
+ case Type::Comment: return ConfigColor::gray;
+ case Type::StringLit: return ConfigColor::lime;
+ case Type::NumberLit: return ConfigColor::amber;
+ case Type::Namespace: return ConfigColor::teal;
+ }
+ return ConfigColor::white;
+}
+
+
+Desc
+generic_For(Type type)
{
switch (type) {
- case TokenType::Keyword: return { "Keyword", "A C/C++ language keyword" };
- case TokenType::Type: return { "Type", "A data type" };
- case TokenType::Identifier: return { "Identifier", "A name in the program" };
- case TokenType::Literal: return { "Literal", "A literal value" };
- case TokenType::Operator: return { "Operator", "An operator" };
- case TokenType::Delimiter: return { "Delimiter", "A delimiter" };
- case TokenType::Preprocessor: return { "Preprocessor", "A preprocessor directive" };
- case TokenType::Function: return { "Function", "A function" };
- case TokenType::Variable: return { "Variable", "A variable" };
- case TokenType::Comment: return { "Comment", "A source comment" };
- case TokenType::StringLit: return { "String", "A string literal" };
- case TokenType::NumberLit: return { "Number", "A numeric literal" };
- case TokenType::Namespace: return { "Namespace", "A namespace" };
+ case Type::Keyword: return { "Keyword", "A C/C++ language keyword" };
+ case Type::Type: return { "Type", "A data type" };
+ case Type::Identifier: return { "Identifier", "A name in the program" };
+ case Type::Literal: return { "Literal", "A literal value" };
+ case Type::Operator: return { "Operator", "An operator" };
+ case Type::Delimiter: return { "Delimiter", "A delimiter" };
+ case Type::Preprocessor: return { "Preprocessor", "A preprocessor directive" };
+ case Type::Function: return { "Function", "A function" };
+ case Type::Variable: return { "Variable", "A variable" };
+ case Type::Comment: return { "Comment", "A source comment" };
+ case Type::StringLit: return { "String", "A string literal" };
+ case Type::NumberLit: return { "Number", "A numeric literal" };
+ case Type::Namespace: return { "Namespace", "A namespace" };
}
return { "", "" };
}
-} // namespace
-
-TokenDB::Desc
-TokenDB::get_Description(const std::string& text, TokenType type)
+Desc
+get_Description(const std::string& text, Type type)
{
const auto& t = table();
auto it = t.find(text);
@@ -130,29 +161,33 @@ TokenDB::get_Description(const std::string& text, TokenType type)
const char*
-TokenDB::get_TypeName(TokenType t)
+get_TypeName(Type t)
{
switch (t) {
- case TokenType::Keyword: return "Keyword";
- case TokenType::Type: return "Type";
- case TokenType::Identifier: return "Identifier";
- case TokenType::Literal: return "Literal";
- case TokenType::Operator: return "Operator";
- case TokenType::Delimiter: return "Delimiter";
- case TokenType::Preprocessor: return "Preprocessor";
- case TokenType::Function: return "Function";
- case TokenType::Variable: return "Variable";
- case TokenType::Comment: return "Comment";
- case TokenType::StringLit: return "String";
- case TokenType::NumberLit: return "Number";
- case TokenType::Namespace: return "Namespace";
+ case Type::Keyword: return "Keyword";
+ case Type::Type: return "Type";
+ case Type::Identifier: return "Identifier";
+ case Type::Literal: return "Literal";
+ case Type::Operator: return "Operator";
+ case Type::Delimiter: return "Delimiter";
+ case Type::Preprocessor: return "Preprocessor";
+ case Type::Function: return "Function";
+ case Type::Variable: return "Variable";
+ case Type::Comment: return "Comment";
+ case Type::StringLit: return "String";
+ case Type::NumberLit: return "Number";
+ case Type::Namespace: return "Namespace";
}
return "Unknown";
}
DynArray<std::string>
-TokenDB::get_TestArrayAllKeywords()
+get_TestArrayAllKeywords()
{
return { "float", "return", "main", "0", "++", "(", ",", ";", "\"", "\"", "#define" };
}
+
+
+} // namespace TokenDB
+} // namespace Text
diff --git a/src/text_3d/text_3d.hpp b/src/text_3d/text_3d.hpp
deleted file mode 100644
index 0890f82..0000000
--- a/src/text_3d/text_3d.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-//
-// src/text_3d/text_3d.hpp — forwarding shim
-//
-// The old `Text3d` is now `Text::Token` (in text/token.hpp). The pointer-level
-// aliases `Text3d` / `Text3dPtr` live in utils/globals.hpp (forward-decl-safe).
-// This header pulls in the full Token definition and adds the aliases that need
-// the complete type: `Text3dAI` and `make_Text3d`.
-//
-// The old src/text_3d/text_3d.cpp is superseded by src/text/token.cpp and MUST
-// be removed from the build (otherwise duplicate Token:: definitions at link).
-
-#include <utility>
-
-#include "cpp_token/token_db.hpp" // parity with the old header's transitive include
-#include "text/token.hpp" // brings the full Text::Token (and globals' Text3d/Text3dPtr)
-
-using Text3dAI = Text::Token::Ai;
-
-template<typename... Args>
-Text3dPtr
-make_Text3d(Args&&... args)
-{
- return Text::make_Token(std::forward<Args>(args)...);
-}
diff --git a/src/utils/frame_profiler.hpp b/src/utils/frame_profiler.hpp
index 9a8274a..3fce987 100644
--- a/src/utils/frame_profiler.hpp
+++ b/src/utils/frame_profiler.hpp
@@ -165,7 +165,7 @@ struct ScopedZone {
} // namespace fp
-// Token-pasting line counter so two FP_ZONEs in the same scope don't collide.
+// Token::Data-pasting line counter so two FP_ZONEs in the same scope don't collide.
#define FP_CONCAT_(a,b) a##b
#define FP_CONCAT(a,b) FP_CONCAT_(a,b)
#define FP_ZONE(name) ::fp::ScopedZone FP_CONCAT(_fp_zone_, __LINE__){name}
diff --git a/src/utils/globals.hpp b/src/utils/globals.hpp
index d225217..295f801 100644
--- a/src/utils/globals.hpp
+++ b/src/utils/globals.hpp
@@ -9,19 +9,28 @@
#include "math.hpp"
-class GlRenderer;
-struct CarriedWordNode;
-struct GameObject;
-namespace Text { struct Token; }
-
using Math::Vec3;
template<typename T>
-using DynArray = std::vector<T>;
+using DynArray = std::vector<T>;
+
+namespace Text {
+ namespace Token { struct Data; }
+ using TokenPtr = cpputils::unique_ptr<Token::Data>;
+}
-using GlRendererPtr = cpputils::unique_ptr<GlRenderer>;
-using CarriedWordNodePtr = cpputils::unique_ptr<CarriedWordNode>;
-using GameObjectPtr = cpputils::unique_ptr<GameObject>;
+namespace Wall {
+ struct TextData;
+ using TextPtr = cpputils::unique_ptr<TextData>;
+ using TextPtrList = DynArray<TextPtr>;
+}
-using Text3d = Text::Token;
-using Text3dPtr = cpputils::unique_ptr<Text::Token>;
+class GlRenderer;
+using GlRendererPtr = cpputils::unique_ptr<GlRenderer>;
+
+struct CarriedWordNode;
+using CarriedWordNodePtr = cpputils::unique_ptr<CarriedWordNode>;
+
+
+struct GameObject;
+using GameObjectPtr = cpputils::unique_ptr<GameObject>;
diff --git a/src/utils/text.cpp b/src/utils/text.cpp
deleted file mode 100644
index 991604f..0000000
--- a/src/utils/text.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-#include "text.hpp"
-
-
-namespace Text
-{
-
-DynArray<std::string>
-wrap_String(const std::string& text, size_t max_chars)
-{
- std::string word;
- std::string current_line;
-
- DynArray<std::string> lines;
-
- auto flush_line = [&]() {
- while (!current_line.empty() && current_line.back() == ' ')
- current_line.pop_back();
- lines.push_back(current_line);
- current_line.clear();
- };
-
- auto flush_word = [&]() {
- if (word.empty()) return;
- if (current_line.size() + word.size() + (current_line.empty() ? 0 : 1)
- > max_chars) {
- flush_line();
- }
- if (!current_line.empty()) current_line += ' ';
- current_line += word;
- word.clear();
- };
-
- for (char c : text) {
- if (c == '\n') {
- flush_word();
- flush_line();
- }
- else if (c == ' ' || c == '\t') {
- flush_word();
- }
- else {
- word += c;
- }
- }
- flush_word();
- flush_line();
-
- return lines;
-}
-
-std::string
-ascii_Sanitize(const std::string& in)
-{
- std::string out;
- out.reserve(in.size());
-
- size_t i = 0;
- while (i < in.size()) {
- const unsigned char c = static_cast<unsigned char>(in[i]);
-
- if (c < 0x80) {
- out += static_cast<char>(c);
- ++i;
- continue;
- }
-
- // U+2000–U+20FF punctuation block: E2 80 xx
- if (c == 0xE2 && i + 2 < in.size() &&
- static_cast<unsigned char>(in[i + 1]) == 0x80) {
- const unsigned char c3 = static_cast<unsigned char>(in[i + 2]);
- switch (c3) {
- case 0x98: case 0x99: out += '\''; break; // ‘ ’
- case 0x9C: case 0x9D: out += '"'; break; // “ ”
- case 0x93: case 0x94: out += '-'; break; // – —
- default: out += '\''; break;
- }
- i += 3;
- continue;
- }
-
- // Any other multibyte sequence: one '?', skip continuation bytes.
- out += '?';
- ++i;
- while (i < in.size() &&
- (static_cast<unsigned char>(in[i]) & 0xC0) == 0x80)
- ++i;
- }
- return out;
-}
-
-
-
-void
-SrcView::clear()
-{ lines.clear(); }
-
-
-std::string
-SrcView::render_Source()
-{
- std::string out;
- for (SrcLine& line : lines) {
- for (size_t ti = 0; ti < line.tokens.size(); ++ti) {
- if (ti) out += ' ';
- SrcToken& tk = line.tokens[ti];
- tk.byte_begin = out.size();
- out += tk.text;
- tk.byte_end = out.size();
- }
- out += '\n';
- }
- return out;
-}
-
-
-SrcToken*
-SrcView::token_At(size_t off)
-{
- for (SrcLine& line : lines)
- for (SrcToken& tk : line.tokens)
- if (off >= tk.byte_begin && off < tk.byte_end)
- return &tk;
- return nullptr;
-}
-
-
-void
-SrcView::apply_Spans(const DynArray<HiSpan>& spans)
-{
- for (SrcLine& line : lines) {
- for (SrcToken& tk : line.tokens) {
- if (!tk.view) continue;
-
- size_t best_overlap = 0;
- TokenType best_type = TokenType::Identifier;
- bool found = false;
- for (const HiSpan& s : spans) {
- size_t lo = (tk.byte_begin > s.byte_begin) ? tk.byte_begin : s.byte_begin;
- size_t hi = (tk.byte_end < s.byte_end) ? tk.byte_end : s.byte_end;
- if (lo < hi) {
- size_t ov = hi - lo;
- if (ov > best_overlap) { best_overlap = ov; best_type = s.type; found = true; }
- }
- }
- if (found) tk.view->set_Color(TokenColor::get(best_type));
- }
- }
-}
-
-DynArray<std::string>
-SrcView::as_Lines() const
-{
- DynArray<std::string> out;
- for (const SrcLine& line : lines) {
- std::string s;
- for (size_t ti = 0; ti < line.tokens.size(); ++ti) {
- if (ti) s += ' ';
- s += line.tokens[ti].text;
- }
- out.push_back(s);
- }
- return out;
-}
-
-} // namespace Text
diff --git a/src/utils/text.hpp b/src/utils/text.hpp
deleted file mode 100644
index aaa1051..0000000
--- a/src/utils/text.hpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#pragma once
-
-#include <string>
-
-#include "cpp_token/cpp_highlight.hpp"
-#include "cpp_token/token_color.hpp"
-#include "text_3d/text_3d.hpp"
-#include "utils/globals.hpp"
-
-
-namespace Text
-{
-
-DynArray<std::string>
- wrap_String(const std::string& text, size_t max_chars);
-
-std::string
- ascii_Sanitize(const std::string& in);
-
-
-// ── Source view (temporary) ────────────────────────────────────────────────
-// Renamed from Token/Line/View to Src* so it no longer collides with the new
-// Text::Token. This whole trio is replaced by Text::Handler and will be deleted
-// once the editor cutover lands; it survives one step purely to keep the build
-// green during the type unification.
-
-struct SrcToken
-{
- std::string text;
- size_t byte_begin { 0 };
- size_t byte_end { 0 };
- Text3d* view { nullptr };
-};
-
-
-struct SrcLine
-{
- DynArray<SrcToken> tokens;
-};
-
-struct SrcView
-{
- DynArray<SrcLine> lines;
-
- void clear ();
- std::string render_Source();
- SrcToken* token_At (size_t off);
- void apply_Spans (const DynArray<HiSpan>& spans);
- DynArray<std::string>
- as_Lines () const;
-};
-
-} // namespace Text
diff --git a/src/wall/editor.cpp b/src/wall/editor.cpp
deleted file mode 100644
index 4d4db46..0000000
--- a/src/wall/editor.cpp
+++ /dev/null
@@ -1,626 +0,0 @@
-#include <SDL3/SDL.h>
-#include <algorithm>
-#include <string>
-
-#include "editor.hpp"
-#include "entities/camera.hpp"
-#include "entities/world.hpp"
-#include "rendering/gl_renderer.hpp"
-
-
-namespace Wall
-{
-
-void
-Editor::clear()
-{
- _lines.clear();
-}
-
-
-Editor::Line*
-Editor::find_NearestLine(double y)
-{
- Line* best = nullptr;
- double best_dy = ConfigWall::snap_threshold_y;
- for (Line& l : _lines) {
- if (l.words.empty()) continue;
- const double center_y = l.words.front()->pos.y; // any word on the row; they share y
- const double dy = SDL_fabs(center_y - y);
- if (dy <= best_dy) { best = &l; best_dy = dy; }
- }
- return best;
-}
-
-
-Editor::Target
-Editor::resolve_Target(double y) const
-{
- DynArray<size_t> order;
- for (size_t i = 0; i < _lines.size(); ++i)
- if (!_lines[i].words.empty()) order.push_back(i);
-
- if (order.empty())
- return { Target::Insert, 0, y };
-
- std::sort(order.begin(), order.end(),
- [&](size_t a, size_t b){ return _lines[a].y > _lines[b].y; });
-
- auto glyph_top = [&](size_t li) {
- return _lines[li].y
- + (double)_lines[li].words.front()->half_extent_y
- + ConfigWall::merge_top_margin;
- };
- auto underline = [&](size_t li) {
- return _lines[li].y - ConfigWall::underline_drop;
- };
-
- // 1) Merge: aim within any line's glyph band [underline .. glyph top].
- for (size_t li : order)
- if (y <= glyph_top(li) && y >= underline(li))
- return { Target::Merge, li, 0.0 };
-
- const double LH = ConfigWall::line_height;
-
- // 2) Above the top line's glyph → new row at the aim height, but never
- // closer than one LH to the current top (keep whatever wider gap the
- // shot asked for).
- {
- const size_t top = order.front();
- if (y > glyph_top(top)) {
- const double min_y = _lines[top].y + LH; // closest allowed
- const double new_y = (y > min_y) ? y : min_y; // keep the wide gap if aimed higher
- return { Target::Insert, top, new_y, 0.0 };
- }
- }
-
- // 3) Between two lines. Decide by where in the gap the aim falls:
- // - enough room and aimed in the middle → place AT the aim (no push)
- // - too close to the upper line → snap one row under it
- // - too close to the lower line → snap one row above it (push lo if needed)
- for (size_t oi = 0; oi + 1 < order.size(); ++oi) {
- const size_t hi = order[oi];
- const size_t lo = order[oi + 1];
- if (y < underline(hi) && y > glyph_top(lo)) {
- const double hi_y = _lines[hi].y;
- const double lo_y = _lines[lo].y;
- const double gap = hi_y - lo_y;
-
- const double free_top = hi_y - LH;
- const double free_bot = lo_y + LH;
-
- // Wide gap, aimed in the open middle → place exactly at the aim,
- // push nothing (there's room on both sides).
- if (gap >= 2.0 * LH && y <= free_top && y >= free_bot)
- return { Target::Insert, lo, y, 0.0 };
-
- // Wide gap but aimed near an edge → snap to that edge, no push
- // (the gap already has room, we just don't free-float there).
- if (gap >= 2.0 * LH)
- return { Target::Insert, lo, (y > free_top) ? free_top : free_bot, 0.0 };
-
- // Tight gap (< 2*LH): only one placement fits. Sit one row under hi
- // and push lo+below down by the deficit to restore LH spacing.
- const double deficit = 2.0 * LH - gap;
- return { Target::Insert, lo, free_top, deficit };
- }
- }
-
- // 4) Below the bottom line → new row at the aim height, but never closer
- // than one LH below the current bottom.
- {
- const size_t bot = order.back();
- const double max_y = _lines[bot].y - LH; // closest allowed (further down = smaller y)
- const double new_y = (y < max_y) ? y : max_y; // keep the wide gap if aimed lower
- return { Target::Insert, bot, new_y, 0.0 };
- }
-}
-
-
-Editor::Line&
-Editor::apply_Target(const Target& t)
-{
- if (t.kind == Target::Merge)
- return _lines[t.line_index];
-
- if (t.push_dist > 0.0 && t.line_index < _lines.size()) {
- const double push_below_y = _lines[t.line_index].y;
- for (Line& l : _lines) {
- if (l.y <= push_below_y + 1e-6) {
- l.y -= t.push_dist;
- for (Text3d* w : l.words) if (w) w->pos.y -= t.push_dist;
- }
- }
- }
-
- Line fresh;
- fresh.y = t.new_y;
- _lines.push_back(std::move(fresh));
- return _lines.back();
-}
-
-
-Editor::Placement
-Editor::place_OnLine(Line& line, double new_half_z, double impact_z)
-{
- if (line.words.empty())
- return { 0, impact_z };
-
- const size_t n = line.words.size();
- auto left_edge = [](const Text3d* w){ return w->pos.z + (double)w->half_extent; };
- auto right_edge = [](const Text3d* w){ return w->pos.z - (double)w->half_extent; };
-
- const Text3d* leftmost = line.words.front();
- const Text3d* rightmost = line.words.back();
-
- // Case A: left of the whole line — always flush, no impact.
- if (impact_z > left_edge(leftmost)) {
- const double flush_z = left_edge(leftmost) + ConfigWall::free_space_dist + new_half_z;
- // Aimed further LEFT than flush (larger z)? keep it. Else snap flush.
- const double z = (impact_z > flush_z) ? impact_z : flush_z;
- return { 0, z }; // front
- }
-
- // Case B: right of the whole line — always flush.
- if (impact_z < right_edge(rightmost)) {
- const double flush_z = right_edge(rightmost) - ConfigWall::free_space_dist - new_half_z;
- // Aimed further RIGHT than flush (smaller z)? keep it. Else snap flush.
- // Never allow larger-than-flush z (that would overlap the neighbour).
- const double z = (impact_z < flush_z) ? impact_z : flush_z;
- return { n, z };
- }
-
- // Case C: between two words.
- for (size_t i = 0; i + 1 < n; ++i) {
- const double gap_left = right_edge(line.words[i]);
- const double gap_right = left_edge(line.words[i + 1]);
- if (impact_z <= gap_left && impact_z >= gap_right) {
- const double needed = 2.0 * new_half_z + 2.0 * ConfigWall::free_space_dist;
- const double avail = gap_left - gap_right;
- if (avail >= needed)
- return { i + 1, (gap_left + gap_right) * 0.5 };
- const double shift = needed - avail;
- for (size_t k = i + 1; k < n; ++k)
- line.words[k]->pos.z -= shift;
- return { i + 1, gap_left - ConfigWall::free_space_dist - new_half_z };
- }
- }
- return { n, right_edge(rightmost) - ConfigWall::free_space_dist - new_half_z };
-}
-
-
-void
-Editor::on_LinePlaced(const DynArray<Text3d*>& words_in, const Vec3& impact)
-{
- if (words_in.empty()) return;
-
- // Visual left→right on the NegX wall is decreasing z (left = larger z).
- DynArray<Text3d*> ws;
- ws.reserve(words_in.size());
- for (Text3d* w : words_in) if (w) ws.push_back(w);
- if (ws.empty()) return;
- std::sort(ws.begin(), ws.end(),
- [](const Text3d* a, const Text3d* b){ return a->pos.z > b->pos.z; });
-
- // Total width of the block: glyph widths + internal gaps.
- double block_w = 0.0;
- for (size_t i = 0; i < ws.size(); ++i) {
- block_w += 2.0 * (double)ws[i]->half_extent;
- if (i + 1 < ws.size()) block_w += ConfigWall::free_space_dist;
- }
-
- Target t = resolve_Target(impact.y);
- Line& targetR = apply_Target(t);
- Line* target = &targetR;
-
- auto left_edge = [](const Text3d* w){ return w->pos.z + (double)w->half_extent; };
- auto right_edge = [](const Text3d* w){ return w->pos.z - (double)w->half_extent; };
-
- // Insert where the aim points: the slot comes after every word left of the
- // aim (the wall's left is +z) and before every word to its right.
- size_t insert_index = 0;
- for (const Text3d* w : target->words)
- if (w && w->pos.z > impact.z) ++insert_index;
-
- const size_t n = target->words.size();
-
- // Centre the block ON the aim, so it merges where the shot actually landed
- // rather than flush against whichever word sits left of the gap.
- double block_left_z = impact.z + block_w * 0.5; // block's left edge (high-z side)
-
- // Don't overlap the word to the LEFT of the slot: pull the block right (−z)
- // if centring would push its left edge into that word.
- if (insert_index > 0) {
- const double max_left = right_edge(target->words[insert_index - 1]) - ConfigWall::free_space_dist;
- if (block_left_z > max_left) block_left_z = max_left;
- }
- // Make room on the RIGHT: if the block would overlap the word to its right,
- // shove that word and everything past it further right (−z).
- if (insert_index < n) {
- const double block_right = block_left_z - block_w;
- const double min_right = left_edge(target->words[insert_index]) + ConfigWall::free_space_dist;
- if (block_right < min_right) {
- const double shift = min_right - block_right;
- for (size_t k = insert_index; k < n; ++k)
- target->words[k]->pos.z -= shift;
- }
- }
-
- // Lay the block out left→right from block_left_z (decreasing z) and snap
- // each word to the row height; keep each word's existing x (wall plane).
- double cursor = block_left_z; // left edge for the next glyph
- for (Text3d* w : ws) {
- const double hw = (double)w->half_extent;
- const double center_z = cursor - hw;
- w->pos.y = target->y;
- w->pos.z = center_z;
- cursor = center_z - hw - ConfigWall::free_space_dist;
- }
-
- // Insert the run into the line in visual order at the chosen slot.
- for (size_t k = 0; k < ws.size(); ++k)
- target->words.insert(
- target->words.begin() + static_cast<std::ptrdiff_t>(insert_index + k),
- ws[k]);
-}
-
-
-void
-Editor::on_WordRemoved(Text3d* word)
-{
- if (!word) return;
-
- for (auto it = _lines.begin(); it != _lines.end(); ) {
- auto& ws = it->words;
- auto wit = std::find(ws.begin(), ws.end(), word);
- if (wit != ws.end()) {
- ws.erase(wit);
- if (ws.empty()) {
- it = _lines.erase(it);
- return;
- }
- return;
- }
- ++it;
- }
-}
-
-
-Vec3
-Editor::on_WordPlaced(Text3d* word, const Vec3& impact_pos)
-{
- if (!word) return impact_pos;
-
- Target t = resolve_Target(impact_pos.y);
- Line& target = apply_Target(t);
-
- const double new_half_z = word->half_extent;
- const Placement p = place_OnLine(target, new_half_z, impact_pos.z);
-
- Vec3 snapped { impact_pos.x, target.y, p.z };
-
- target.words.insert(
- target.words.begin() + static_cast<std::ptrdiff_t>(p.insert_index),
- word);
-
- return snapped;
-}
-
-
-void
-Editor::add_PlacedLine(const DynArray<Text3d*>& words, double row_y)
-{
- if (words.empty()) return;
- Line line;
- line.y = row_y;
- for (Text3d* w : words)
- if (w) line.words.push_back(w);
- _lines.push_back(std::move(line));
-}
-
-
-// ─────────────────────────────────────────────────────────────────────────────
-// Line numbers
-// ─────────────────────────────────────────────────────────────────────────────
-
-namespace {
-
-Text3dPtr
-make_Number(const std::string& s, const Vec3& pos, float yaw)
-{
- // The Text3d constructor colours by token lookup (e.g. "1" is a Literal →
- // red), so force orange afterwards.
- Text3dPtr t = make_Text3d(s, Text3d::depth_wall,
- ConfigWall::line_number_scale, pos,
- ConfigColor::orange);
- t->yaw = yaw;
- t->set_Color(ConfigColor::orange);
- return t;
-}
-
-void
-set_NumberText(Text3d* t, const std::string& s)
-{
- if (t->text == s) return;
- t->text = s;
- t->rebuild_Mesh();
- t->set_Color(ConfigColor::orange); // rebuild can reset colour; keep orange
-}
-
-} // namespace
-
-
-void
-Editor::update(double dt, const Vec3& eye_pos)
-{
- if (_lines.empty()) return;
-
- // Order rows top→bottom (largest y = line 1) to assign numbers.
- DynArray<size_t> order;
- order.reserve(_lines.size());
- for (size_t i = 0; i < _lines.size(); ++i)
- if (!_lines[i].words.empty()) order.push_back(i);
-
- std::sort(order.begin(), order.end(),
- [&](size_t a, size_t b){ return _lines[a].y > _lines[b].y; });
-
- const Transform tf = get_Transform(Role::Editor, World::get_BoxScale());
-
- if (order.empty()) return;
-
- // Global code extents (z) across every row, so both number columns land at
- // a single aligned position with a clear, uniform gap from the code.
- double code_left = -1e18; // furthest-left z code reaches (visual left = +z)
- double code_right = 1e18; // furthest-right z code reaches (visual right = -z)
- for (size_t oi : order)
- for (Text3d* w : _lines[oi].words) {
- if (!w) continue;
- code_left = SDL_max(code_left, w->pos.z + (double)w->half_extent);
- code_right = SDL_min(code_right, w->pos.z - (double)w->half_extent);
- }
-
- // Pass 1: ensure labels exist, refresh digits, advance the bounce, and find
- // the widest digit so the clamp below can keep every number on the wall.
- double max_lh = 0.0, max_rh = 0.0;
- int rank = 1;
- for (size_t oi : order) {
- Line& l = _lines[oi];
-
- const std::string s = std::to_string(rank);
- if (!l.num_left) l.num_left = make_Number(s, Vec3{tf.position.x, l.y, 0.0}, tf.yaw);
- if (!l.num_right) l.num_right = make_Number(s, Vec3{tf.position.x, l.y, 0.0}, tf.yaw);
-
- if (l.shown_number != rank) {
- set_NumberText(l.num_left.get(), s);
- set_NumberText(l.num_right.get(), s);
- l.shown_number = rank;
- }
-
- // Damped spring bounce back to rest (shot numbers jiggle, stay put).
- constexpr double STIFFNESS = 120.0, DAMPING = 12.0;
- l.bounce_vel += (-STIFFNESS * l.bounce_off - DAMPING * l.bounce_vel) * dt;
- l.bounce_off += l.bounce_vel * dt;
-
- max_lh = SDL_max(max_lh, static_cast<double>(l.num_left ->half_extent));
- max_rh = SDL_max(max_rh, static_cast<double>(l.num_right->half_extent));
- ++rank;
- }
-
- // Code-relative columns, but CLAMPED so a number can never leave the wall.
- // The wall spans z in [-ws, +ws]; keep a small margin in from each edge.
- // A left number occupies [left_col_z, left_col_z + 2*lh] (outer edge = +z);
- // a right number occupies [right_col_z - 2*rh, right_col_z] (outer = -z).
- const double ws = World::get_BoxScale();
- const double EDGE = 0.3;
- double left_col_z = code_left + ConfigWall::line_number_gap;
- double right_col_z = code_right - ConfigWall::line_number_gap;
- left_col_z = SDL_min(left_col_z, ws - EDGE - 2.0 * max_lh);
- right_col_z = SDL_max(right_col_z, -ws + EDGE + 2.0 * max_rh);
-
- // Pass 2: place each number against its column at its (bounced) row height.
- for (size_t oi : order) {
- Line& l = _lines[oi];
-
- // Use a code word's x so the numbers sit on the wall plane at the
- // same depth as the text.
- double x = tf.position.x;
- for (Text3d* w : l.words) { if (w) { x = w->pos.x; break; } }
-
- const double ny = l.y + l.bounce_off;
- const double lh = static_cast<double>(l.num_left-> half_extent);
- const double rh = static_cast<double>(l.num_right->half_extent);
- l.num_left->pos = Vec3{ x, ny, left_col_z + lh }; // code-facing edge on the column
- l.num_right->pos = Vec3{ x, ny, right_col_z - rh };
-
- l.num_left->pitch = Text::tilt_TowardEye(l.num_left->pos, eye_pos);
- l.num_right->pitch = Text::tilt_TowardEye(l.num_right->pos, eye_pos);
- }
-}
-
-
-DynArray<DynArray<Text3d*>>
-Editor::lines_InOrder() const
-{
- // Order rows top→bottom (largest y first), matching update()'s numbering.
- DynArray<size_t> order;
- order.reserve(_lines.size());
- for (size_t i = 0; i < _lines.size(); ++i)
- if (!_lines[i].words.empty()) order.push_back(i);
-
- std::sort(order.begin(), order.end(),
- [&](size_t a, size_t b){ return _lines[a].y > _lines[b].y; });
-
- DynArray<DynArray<Text3d*>> out;
- out.reserve(order.size());
- for (size_t oi : order) {
- // Within a row, visual left→right is decreasing z (left = larger z).
- DynArray<Text3d*> ws;
- ws.reserve(_lines[oi].words.size());
- for (Text3d* w : _lines[oi].words)
- if (w) ws.push_back(w);
- std::sort(ws.begin(), ws.end(),
- [](const Text3d* a, const Text3d* b){ return a->pos.z > b->pos.z; });
- out.push_back(std::move(ws));
- }
- return out;
-}
-
-
-DynArray<Editor::NumberHit>
-Editor::number_Hitboxes() const
-{
- // Build the box live from the label's current pos + half-extents rather
- // than the base aabb_*() (which can lag a constantly-repositioned object).
- // Pad generously: a single digit is a small target.
- constexpr double PAD = 0.30;
- constexpr double DEPTH_X = 0.50; // thickness into the room so shots cross it
-
- DynArray<NumberHit> out;
- out.reserve(_lines.size() * 2);
-
- auto push_box = [&](const Text3d* t, size_t i) {
- const double hz = static_cast<double>(t->half_extent ) + PAD; // along wall (z)
- const double hy = static_cast<double>(t->half_extent_y) + PAD; // vertical
- const Vec3 c = t->pos;
- out.push_back({ HIT_BASE + i,
- Vec3{ c.x - DEPTH_X, c.y - hy, c.z - hz },
- Vec3{ c.x + DEPTH_X, c.y + hy, c.z + hz } });
- };
-
- for (size_t i = 0; i < _lines.size(); ++i) {
- const Line& l = _lines[i];
- if (l.num_left) push_box(l.num_left.get(), i);
- if (l.num_right) push_box(l.num_right.get(), i);
- }
- return out;
-}
-
-
-double
-Editor::scroll_DeltaToEye(size_t line_id, double eye_y) const
-{
- if (line_id >= _lines.size()) return 0.0;
- return eye_y - _lines[line_id].y;
-}
-
-
-void
-Editor::bounce(size_t line_id)
-{
- if (line_id < _lines.size())
- _lines[line_id].bounce_vel += ConfigWall::bounce_impulse;
-}
-
-
-void
-Editor::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
-{
- if (_lines.empty()) return;
-
- DynArray<float> verts;
- DynArray<int> indices;
- verts .reserve(_lines.size() * 4 * 9);
- indices.reserve(_lines.size() * 6);
-
- /*
- const float ws = World::get_BoxScale();
- const float x_front = -ws + 0.02f;
-
- constexpr float nx = 1.0f, ny = 0.0f, nz = 0.0f;
- constexpr float cr = 0.40f, cg = 0.50f, cb = 0.78f;
-
- int base = 0;
- for (const Line& l : _lines) {
- if (l.words.empty()) continue;
-
- const float y_mid = static_cast<float>(l.y - ConfigWall::underline_drop);
- const float y_lo = y_mid - static_cast<float>(ConfigWall::underline_thick);
- const float y_hi = y_mid + static_cast<float>(ConfigWall::underline_thick);
-
- float pts[4][3] = {
- { x_front, y_lo, -ws }, // BL
- { x_front, y_lo, ws }, // BR
- { x_front, y_hi, ws }, // TR
- { x_front, y_hi, -ws }, // TL
- };
-
- for (int v = 0; v < 4; ++v) {
- verts.push_back(pts[v][0]);
- verts.push_back(pts[v][1]);
- verts.push_back(pts[v][2]);
- verts.push_back(nx); verts.push_back(ny); verts.push_back(nz);
- verts.push_back(cr); verts.push_back(cg); verts.push_back(cb);
- }
-
- indices.push_back(base + 0);
- indices.push_back(base + 1);
- indices.push_back(base + 2);
- indices.push_back(base + 0);
- indices.push_back(base + 2);
- indices.push_back(base + 3);
- base += 4;
- }
-
- if (!verts.empty()) {
- Mat4 view = buildViewMat4(cam);
- Mat4 proj = gl.projection();
- Mat4 model = Mat4::identity();
- Mat4 mvp = Mat4::mul(proj, Mat4::mul(view, model));
-
- gl.set_Wireframe(false);
- gl.set_MVP(model, mvp);
- gl.set_NormalMat(model);
- gl.set_Light(light);
- gl.set_ColorUseVertex(true);
-
- gl.draw_MeshColored(verts.data(),
- static_cast<int>(verts.size() / 9),
- indices.data(),
- static_cast<int>(indices.size()));
-
- gl.set_ColorUseVertex(false);
- }
- */
-
- // Orange line numbers (own meshes; positioned/numbered in update()).
- for (const Line& l : _lines) {
- if (l.num_left) l.num_left->draw(gl, cam, light);
- if (l.num_right) l.num_right->draw(gl, cam, light);
- }
-}
-
-
-double
-Editor::get_GridOrigin() const
-{
- double origin = 0.0;
- bool found = false;
- for (const Line& l : _lines) {
- if (l.words.empty()) continue;
- if (!found || l.y > origin) { origin = l.y; found = true; }
- }
- return found ? origin : 0.0;
-}
-
-double
-Editor::snap_RowY(double y) const
-{
- const double LH = ConfigWall::line_height;
- const double origin = get_GridOrigin();
- const double row = std::round((y - origin) / LH);
- return origin + row * LH;
-}
-
-bool
-Editor::row_IsEmpty(double y) const
-{
- const double LH = ConfigWall::line_height;
- const double slot_y = snap_RowY(y);
- for (const Line& l : _lines) {
- if (l.words.empty()) continue;
- if (SDL_fabs(l.y - slot_y) < LH * 0.5) return false;
- }
- return true;
-}
-
-} // namepsace Wall
diff --git a/src/wall/editor.hpp b/src/wall/editor.hpp
deleted file mode 100644
index 915fb42..0000000
--- a/src/wall/editor.hpp
+++ /dev/null
@@ -1,84 +0,0 @@
-#pragma once
-
-#include "text_3d/text_3d.hpp"
-#include "utils/globals.hpp"
-#include "utils/light.hpp"
-#include "wall.hpp"
-
-class Camera;
-class GlRenderer;
-
-namespace Wall {
-
-class Editor
-{
- struct Line
- {
- int shown_number {-1};
- double y {0.0};
- double bounce_off {0.0};
- double bounce_vel {0.0};
-
- Text3dPtr num_left;
- Text3dPtr num_right;
-
- DynArray<Text3d*> words;
- };
-
- struct Placement
- {
- size_t insert_index;
- double z;
- };
-
- struct Target
- {
- enum Kind { Merge, Insert } kind;
- size_t line_index;
- double new_y;
- double push_dist {0.0};
- };
-
- DynArray<Line> _lines;
-
- Line* find_NearestLine (double y);
- Placement place_OnLine (Line& line, double new_half_z, double impact_z);
- double get_GridOrigin () const;
- Target resolve_Target (double y) const;
- Line& apply_Target (const Target& t);
-public:
- static constexpr size_t
- HIT_BASE = size_t(1) << 32;
- static bool is_LineNumberHit(size_t idx) { return idx >= HIT_BASE; }
- static size_t line_Of (size_t idx) { return idx - HIT_BASE; }
-
-
- struct NumberHit {
- size_t index;
- Vec3 aabb_Min;
- Vec3 aabb_Max;
- };
-
- void clear ();
- void update (double dt, const Vec3& eye_pos);
- void draw (GlRenderer& gl, const Camera& cam, const Light& light) const;
- void bounce (size_t line_id);
- void scroll(double delta) { for (Line& l : _lines) l.y += delta; }
-
- void on_LinePlaced (const DynArray<Text3d*>& words, const Vec3& impact);
- void on_WordRemoved(Text3d* word);
- Vec3 on_WordPlaced (Text3d* word, const Vec3& impact_pos);
- void add_PlacedLine(const DynArray<Text3d*>& words, double row_y);
-
- bool row_IsEmpty(double y) const;
- double snap_RowY (double y) const;
- double scroll_DeltaToEye(size_t line_id, double eye_y) const;
- size_t line_Count () const { return _lines.size(); }
- DynArray<DynArray<Text3d*>>
- lines_InOrder () const;
-
- DynArray<NumberHit>
- number_Hitboxes () const;
-};
-
-} // namespace Wall
diff --git a/src/wall/label.cpp b/src/wall/label.cpp
index d2e4ee1..4acb157 100644
--- a/src/wall/label.cpp
+++ b/src/wall/label.cpp
@@ -49,9 +49,9 @@ Wall::Label::generate_All(float world_scale)
const double extra_offset = 2.5f;
// Large scale so the name is readable from across the room.
- // depth is Text3d::depth_wall * 4 for chunky, visible letters.
+ // depth is Text::Token::depth_wall * 4 for chunky, visible letters.
const float label_scale = 0.012f;
- const float label_depth = Text3d::depth_wall * 4.0f;
+ const float label_depth = Text::Token::depth_wall * 4.0f;
for (uint8_t ri = 0; ri < 4; ++ri) {
Wall::Role role = static_cast<Wall::Role> (ri);
@@ -109,10 +109,10 @@ Wall::Label::update(double dt, const Camera& camera)
label.update(dt, camera);
// Tilt up/down toward the player's eye, clamped — identical to the
- // editor wall text tilt in wall.hpp::tilt_TowardEye. Shared anchor pitch
+ // editor wall text tilt in wall.hpp::tilt_TwardEye. Shared anchor pitch
// keeps the whole word reading as one rigid plane.
- const float pitch = Wall::Text::tilt_TowardEye(anchor, camera.pos);
- for (const Text3dPtr& w : label.get_Words())
+ const float pitch = Wall::tilt_TwardEye(anchor, camera.pos);
+ for (const Text::TokenPtr& w : label.get_Words())
if (w) w->pitch = pitch;
}
diff --git a/src/wall/label.hpp b/src/wall/label.hpp
index 38d9415..95f186f 100644
--- a/src/wall/label.hpp
+++ b/src/wall/label.hpp
@@ -18,7 +18,7 @@ namespace Wall
Wall::Role role;
float face_yaw;
Vec3 anchor;
- ::Text::Handler label;
+ Text::Handler label;
static DynArray<Label> generate_All(float world_scale);
diff --git a/src/wall/text.cpp b/src/wall/text.cpp
deleted file mode 100644
index 6bc906d..0000000
--- a/src/wall/text.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "entities/world.hpp"
-#include "systems/cpp_highlight_system.hpp"
-#include "text/handler.hpp"
-#include "utils/config.hpp"
-#include "wall/wall.hpp"
-
-
-namespace Wall {
-
-Ptr
-Text::from_Drop(Text3dPtr src)
-{
- double ws = World::get_BoxScale<double>();
- double x = src->pos.x;
- double z = src->pos.z;
-
- Vec3& he = src->shape.half_extents;
-
- double dPX = ws - x;
- double dNX = ws + x;
- double dPZ = ws - z;
- double dNZ = ws + z;
-
- Wall::Position wall_pos;
- float yaw;
-
-
- // he.x is the text WIDTH half-extent, he.z is the text DEPTH (thickness).
- // After yaw rotation, the depth axis becomes the wall-normal axis.
- // For X walls the word is rotated 90°, so its local Z (depth) points along
- // world X — we offset by he.z to sit flush against the wall.
- // For Z walls the word faces straight on, so local Z (depth) points along
- // world Z — same he.z offset.
- double wo = static_cast<double>(ConfigWall::text_offset);
- if (dPX <= dNX && dPX <= dPZ && dPX <= dNZ) { wall_pos = Wall::Position::PosX; src->pos.x = ws - he.z - wo; yaw = -SDL_PI_F * 0.5f; }
- else if (dNX <= dPZ && dNX <= dNZ) { wall_pos = Wall::Position::NegX; src->pos.x = -ws + he.z + wo; yaw = SDL_PI_F * 0.5f; }
- else if (dPZ <= dNZ) { wall_pos = Wall::Position::PosZ; src->pos.z = ws - he.z - wo; yaw = SDL_PI_F; }
- else { wall_pos = Wall::Position::NegZ; src->pos.z = -ws + he.z + wo; yaw = 0.0f; }
-
- src->yaw = yaw;
-
- Wall::Role role = Wall::role_Of(wall_pos);
-
- return cpputils::make_unique<Text>(Text{
- wall_pos,
- role,
- std::move(src)
- });
-}
-
-
-std::string
-Text::extract_Text(const PtrList& texts, Wall::Role role, double line_threshold)
-{
- DynArray<Text*> items;
- items.reserve(texts.size());
-
- for (const Ptr& wt : texts) {
- if (wt->role == role)
- items.push_back(wt.get());
- }
-
- if (items.empty())
- return "";
-
- const Wall::Position wall_pos = Wall::position_Of(role);
-
- std::sort(items.begin(), items.end(),
- [](const Text* a, const Text* b) {
- return a->text3d->pos.y > b->text3d->pos.y;
- });
-
- struct Line {
- DynArray<Text*> items;
- double sum_y = 0.0;
- };
-
- DynArray<Line> lines;
- lines.reserve(items.size());
-
- for (Text* wt : items) {
- const double y = wt->text3d->pos.y;
-
- if (!lines.empty()) {
- Line& line = lines.back();
- const double avg_y = line.sum_y / static_cast<double>(line.items.size());
-
- if (SDL_fabs(y - avg_y) < line_threshold) {
- line.items.push_back(wt);
- line.sum_y += y;
- continue;
- }
- }
-
- lines.push_back(Line{ {wt}, y });
- }
-
- for (Line& line : lines) {
- std::sort(line.items.begin(), line.items.end(),
- [&](const Text* a, const Text* b) {
- if (wall_pos == Wall::Position::PosX ||
- wall_pos == Wall::Position::NegX)
- return a->text3d->pos.z > b->text3d->pos.z;
- else
- return a->text3d->pos.x > b->text3d->pos.x;
- });
- }
-
- std::string result;
- for (const Line& line : lines) {
- for (size_t i = 0; i < line.items.size(); ++i) {
- if (i) result += ' ';
-
- const Text* wt = line.items[i];
- if (wt->text3d->type == ObjectType::Text)
- result += wt->text3d->text;
- }
- result += '\n';
- }
-
- return result;
-}
-
-
-void
-Text::draw(GlRenderer& g, const Camera& c, const Light& l) const
-{
- text3d->draw(g, c, l);
-}
-
-
-void
-Text::show_TokenInfo(::Text::Handler& info, const std::string& token_text)
-{
- info.clear();
-
- TokenType tt = CppHighlight::classify_Word(token_text);
- TokenDB::Desc d = TokenDB::get_Description(token_text, tt);
-
- const std::string line1 = token_text + " [" + TokenDB::get_TypeName(tt) + "]";
- const std::string line2 = d.short_desc;
- const std::string line3 = d.long_desc;
-
- // Bottom-aligned: each append pushes the prior lines up, so line1 ends on top.
- for (const std::string& line : { line1, line2, line3 })
- if (!line.empty()) info.append_Line(line);
-}
-
-
-float
-Text::tilt_TowardEye(const Vec3& text_pos, const Vec3& eye_pos)
-{
- double dx = eye_pos.x - text_pos.x;
- double dy = eye_pos.y - text_pos.y;
- double dz = eye_pos.z - text_pos.z;
-
- double horiz = SDL_sqrt(dx*dx + dz*dz);
- if (horiz < 0.01) return 0.0f; // player directly above/below — no tilt
-
- // Sign flipped: we want positive pitch when player is BELOW the text.
- double pitch = -SDL_atan2(dy, horiz);
-
- if (pitch > static_cast<double>(ConfigWall::max_tilt)) pitch = ConfigWall::max_tilt;
- if (pitch < -static_cast<double>(ConfigWall::max_tilt)) pitch = -ConfigWall::max_tilt;
- return static_cast<float>(pitch);
-}
-
-
-} // namespace Wall
diff --git a/src/wall/wall.cpp b/src/wall/wall.cpp
index 2e645f5..3a9eeb1 100644
--- a/src/wall/wall.cpp
+++ b/src/wall/wall.cpp
@@ -1,4 +1,6 @@
+#include "text/token.hpp"
#include "entities/world.hpp"
+#include "text/handler.hpp"
#include "wall.hpp"
namespace Wall
@@ -74,4 +76,70 @@ namespace Wall
{
return get_Transform(role, world_scale).position;
}
+
+
+ Drop
+ attach_ToNearestWall(Text::TokenPtr src)
+ {
+ const double ws = World::get_BoxScale<double>();
+ const double x = src->pos.x;
+ const double z = src->pos.z;
+
+ Vec3& he = src->shape.half_extents;
+
+ const double dPX = ws - x;
+ const double dNX = ws + x;
+ const double dPZ = ws - z;
+ const double dNZ = ws + z;
+
+ Wall::Position wall_pos;
+ float yaw;
+
+ const double wo = static_cast<double>(ConfigWall::text_offset);
+ if (dPX <= dNX && dPX <= dPZ && dPX <= dNZ) { wall_pos = Wall::Position::PosX; src->pos.x = ws - he.z - wo; yaw = -SDL_PI_F * 0.5f; }
+ else if (dNX <= dPZ && dNX <= dNZ) { wall_pos = Wall::Position::NegX; src->pos.x = -ws + he.z + wo; yaw = SDL_PI_F * 0.5f; }
+ else if (dPZ <= dNZ) { wall_pos = Wall::Position::PosZ; src->pos.z = ws - he.z - wo; yaw = SDL_PI_F; }
+ else { wall_pos = Wall::Position::NegZ; src->pos.z = -ws + he.z + wo; yaw = 0.0f; }
+
+ src->yaw = yaw;
+
+ return Drop{ Wall::role_Of(wall_pos), std::move(src) };
+ }
+
+
+ void
+ show_TokenInfo(::Text::Handler& info, const std::string& token_text)
+ {
+ info.clear();
+
+ Text::Token::Type tt = Text::CppHighlight::classify_Word(token_text);
+ Text::Token::Desc d = Text::Token::get_Description(token_text, tt);
+
+ const std::string line1 = token_text + " [" + Text::Token::get_TypeName(tt) + "]";
+ const std::string line2 = d.short_desc;
+ const std::string line3 = d.long_desc;
+
+ for (const std::string& line : { line1, line2, line3 })
+ if (!line.empty()) info.append_Line(line);
+ }
+
+
+ float
+ tilt_TwardEye(const Vec3& text_pos, const Vec3& eye_pos)
+ {
+ const double dx = eye_pos.x - text_pos.x;
+ const double dy = eye_pos.y - text_pos.y;
+ const double dz = eye_pos.z - text_pos.z;
+
+ const double horiz = SDL_sqrt(dx*dx + dz*dz);
+ if (horiz < 0.01) return 0.0f;
+
+ double pitch = -SDL_atan2(dy, horiz);
+
+ if (pitch > static_cast<double>(ConfigWall::max_tilt)) pitch = ConfigWall::max_tilt;
+ if (pitch < -static_cast<double>(ConfigWall::max_tilt)) pitch = -ConfigWall::max_tilt;
+ return static_cast<float>(pitch);
+ }
+
+
} // namespace Wall
diff --git a/src/wall/wall.hpp b/src/wall/wall.hpp
index 3f4ca42..0d22e68 100644
--- a/src/wall/wall.hpp
+++ b/src/wall/wall.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "text_3d/text_3d.hpp"
+#include "text/token.hpp"
#include "utils/config.hpp"
#include "utils/globals.hpp"
@@ -9,16 +9,25 @@ namespace Text { class Handler; }
namespace Wall
{
- struct Text;
- using Ptr = cpputils::unique_ptr<Text>;
- using PtrList = DynArray<Ptr>;
-
enum class Position : uint8_t { PosX, NegX, PosZ, NegZ };
enum class Role : uint8_t { Program, Editor, Info, Compiler };
- Role role_Of (Position p);
- Position position_Of (Role r);
- Vec3 inward_Normal(Wall::Position pos);
+ class Surface;
+ struct Drop;
+ struct Label;
+ struct Transform;
+}
+
+namespace Wall
+{
+
+ Role role_Of (Position p);
+ Position position_Of (Role r);
+ Vec3 inward_Normal (Wall::Position pos);
+
+ Drop attach_ToNearestWall(Text::TokenPtr src);
+ void show_TokenInfo (::Text::Handler& info, const std::string& token_text);
+ float tilt_TwardEye (const Vec3& text_pos, const Vec3& eye_pos);
struct Transform
{
@@ -36,7 +45,7 @@ namespace Wall
public:
struct Quad {
bool is_editor;
- Wall::Role role;
+ Role role;
SDL_FColor color;
DynArray<float> verts;
@@ -49,18 +58,11 @@ namespace Wall
DynArray<Quad> _walls;
};
- struct Text
+ struct Drop
{
- Wall::Position wall_position;
- Wall::Role role;
- Text3dPtr text3d;
-
- void draw (GlRenderer& gl, const Camera& camera, const Light& light) const;
-
- static void show_TokenInfo(::Text::Handler& info, const std::string& token_text);
- static Ptr from_Drop (Text3dPtr src);
- static std::string extract_Text (const PtrList&, Wall::Role, double line_threshold = 0.3);
- static float tilt_TowardEye(const Vec3& text_pos, const Vec3& eye_pos);
+ Role role;
+ Text::TokenPtr token;
};
-}
+
+} // namespace Wall