X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fui%2Fui.cpp;h=037fcb510dc16a55d96f1ca638d08ba48fe47229;hb=e872614d387c4bfc3afb04bcc7cba3d9b8f3954b;hp=88190e643d57b05f6bf9dba6e9db9b617ea8bfdd;hpb=37a1465a83e4ac4363ed0d8e0fa1ce5055dd2db4;p=blank.git diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index 88190e6..037fcb5 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -1,13 +1,17 @@ #include "HUD.hpp" #include "Interface.hpp" +#include "Keymap.hpp" #include "../app/Assets.hpp" +#include "../app/Environment.hpp" #include "../app/FrameCounter.hpp" #include "../app/init.hpp" -#include "../graphics/BlendedSprite.hpp" -#include "../graphics/DirectionalLighting.hpp" +#include "../audio/Audio.hpp" #include "../graphics/Font.hpp" +#include "../graphics/Viewport.hpp" +#include "../io/TokenStreamReader.hpp" #include "../model/shapes.hpp" +#include "../world/BlockLookup.hpp" #include "../world/World.hpp" #include @@ -27,40 +31,31 @@ HUD::HUD(const BlockTypeRegistry &types, const Font &font) , block_buf() , block_transform(1.0f) , block_label() -, label_sprite() -, label_transform(1.0f) -, label_color{0xFF, 0xFF, 0xFF, 0xFF} , block_visible(false) -, crosshair() -, crosshair_transform(1.0f) -, near(100.0f) -, far(-100.0f) -, projection(glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, near, far)) -, view(1.0f) { +, crosshair() { block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f)); block_transform = glm::scale(block_transform, glm::vec3(50.0f)); block_transform = glm::rotate(block_transform, 3.5f, glm::vec3(1.0f, 0.0f, 0.0f)); block_transform = glm::rotate(block_transform, 0.35f, glm::vec3(0.0f, 1.0f, 0.0f)); - crosshair.vertices = std::vector({ + OutlineModel::Buffer buf; + buf.vertices = std::vector({ { -10.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f }, { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, 0.0f }, }); - crosshair.indices = std::vector({ + buf.indices = std::vector({ 0, 1, 2, 3 }); - crosshair.colors.resize(4, { 10.0f, 10.0f, 10.0f }); - crosshair.Invalidate(); -} - - -void HUD::Viewport(float width, float height) noexcept { - Viewport(0, 0, width, height); -} + buf.colors.resize(4, { 10.0f, 10.0f, 10.0f }); + crosshair.Update(buf); -void HUD::Viewport(float x, float y, float width, float height) noexcept { - projection = glm::ortho(x, width, height, y, near, far); - crosshair_transform = glm::translate(glm::vec3(width * 0.5f, height * 0.5f, 0.0f)); + block_label.Position( + glm::vec3(50.0f, 85.0f, 0.0f), + Gravity::NORTH_WEST, + Gravity::NORTH + ); + block_label.Foreground(glm::vec4(1.0f)); + block_label.Background(glm::vec4(0.5f)); } @@ -68,75 +63,89 @@ void HUD::Display(const Block &b) { const BlockType &type = types.Get(b.type); block_buf.Clear(); - type.FillModel(block_buf, b.Transform()); + type.FillEntityModel(block_buf, b.Transform()); block.Update(block_buf); - font.Render(type.label.c_str(), label_color, block_label); - glm::vec2 size(font.TextSize(type.label.c_str())); - label_sprite.LoadRect(size.x, size.y); - label_transform = glm::translate(glm::vec3( - std::max(5.0f, 50.0f - std::round(size.x * 0.5f)), - 70.0f + size.y, - 0.75f - )); + block_label.Set(font, type.label); block_visible = type.visible; } -void HUD::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept { - world_prog.Activate(); - world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f }); - // disable distance fog - world_prog.SetFogDensity(0.0f); - GLContext::ClearDepthBuffer(); +void HUD::Render(Viewport &viewport) noexcept { + viewport.ClearDepth(); - GLContext::EnableInvertBlending(); - world_prog.SetMVP(crosshair_transform, view, projection); + PlainColor &outline_prog = viewport.HUDOutlineProgram(); + viewport.EnableInvertBlending(); + viewport.SetCursor(glm::vec3(0.0f), Gravity::CENTER); + outline_prog.SetM(viewport.Cursor()); crosshair.Draw(); if (block_visible) { - GLContext::DisableBlending(); + DirectionalLighting &world_prog = viewport.HUDProgram(); + world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f }); + // disable distance fog + world_prog.SetFogDensity(0.0f); + + viewport.DisableBlending(); world_prog.SetM(block_transform); block.Draw(); - - sprite_prog.Activate(); - sprite_prog.SetMVP(label_transform, view, projection); - sprite_prog.SetTexture(block_label); - label_sprite.Draw(); + block_label.Render(viewport); } } Interface::Interface( const Config &config, - const Assets &assets, - const FrameCounter &counter, + Environment &env, World &world) -: counter(counter) +: env(env) , world(world) , ctrl(world.Player()) -, font(assets.LoadFont("DejaVuSans", 16)) -, hud(world.BlockTypes(), font) +, hud(world.BlockTypes(), env.assets.small_ui_font) , aim{{ 0, 0, 0 }, { 0, 0, -1 }} -, aim_chunk(nullptr) -, aim_block(0) -, aim_normal() +, aim_world() +, aim_entity() , outline() , outline_transform(1.0f) -, show_counter(false) -, counter_tex() -, counter_sprite() -, counter_transform(1.0f) -, counter_color{0xFF, 0xFF, 0xFF, 0xFF} +, counter_text() +, position_text() +, orientation_text() +, block_text() +, last_block() +, last_entity(nullptr) +, messages(env.assets.small_ui_font) +, msg_timer(5000) , config(config) , place_timer(256) , remove_timer(256) , remove(0) , selection(1) +, place_sound(env.assets.LoadSound("thump")) +, remove_sound(env.assets.LoadSound("plop")) , fwd(0) -, rev(0) { - hud.Viewport(960, 600); +, rev(0) +, debug(false) { + counter_text.Position(glm::vec3(-25.0f, 25.0f, 0.0f), Gravity::NORTH_EAST); + counter_text.Foreground(glm::vec4(1.0f)); + counter_text.Background(glm::vec4(0.5f)); + position_text.Position(glm::vec3(-25.0f, 25.0f + env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST); + position_text.Foreground(glm::vec4(1.0f)); + position_text.Background(glm::vec4(0.5f)); + orientation_text.Position(glm::vec3(-25.0f, 25.0f + 2 * env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST); + orientation_text.Foreground(glm::vec4(1.0f)); + orientation_text.Background(glm::vec4(0.5f)); + block_text.Position(glm::vec3(-25.0f, 25.0f + 4 * env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST); + block_text.Foreground(glm::vec4(1.0f)); + block_text.Background(glm::vec4(0.5f)); + block_text.Set(env.assets.small_ui_font, "Block: none"); + entity_text.Position(glm::vec3(-25.0f, 25.0f + 4 * env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST); + entity_text.Foreground(glm::vec4(1.0f)); + entity_text.Background(glm::vec4(0.5f)); + entity_text.Set(env.assets.small_ui_font, "Entity: none"); + messages.Position(glm::vec3(25.0f, -25.0f, 0.0f), Gravity::SOUTH_WEST); + messages.Foreground(glm::vec4(1.0f)); + messages.Background(glm::vec4(0.5f)); hud.Display(selection); } @@ -144,52 +153,81 @@ Interface::Interface( void Interface::HandlePress(const SDL_KeyboardEvent &event) { if (config.keyboard_disabled) return; - switch (event.keysym.sym) { - case SDLK_w: + switch (env.keymap.Lookup(event)) { + case Keymap::MOVE_FORWARD: rev.z = 1; break; - case SDLK_s: + case Keymap::MOVE_BACKWARD: fwd.z = 1; break; - case SDLK_a: + case Keymap::MOVE_LEFT: rev.x = 1; break; - case SDLK_d: + case Keymap::MOVE_RIGHT: fwd.x = 1; break; - case SDLK_SPACE: + case Keymap::MOVE_UP: fwd.y = 1; break; - case SDLK_LSHIFT: + case Keymap::MOVE_DOWN: rev.y = 1; break; - case SDLK_q: + case Keymap::BLOCK_FACE: FaceBlock(); break; - case SDLK_e: + case Keymap::BLOCK_TURN: TurnBlock(); break; + case Keymap::BLOCK_NEXT: + SelectNext(); + break; + case Keymap::BLOCK_PREV: + SelectPrevious(); + break; + + case Keymap::BLOCK_PLACE: + PlaceBlock(); + break; + case Keymap::BLOCK_PICK: + PickBlock(); + break; + case Keymap::BLOCK_REMOVE: + RemoveBlock(); + break; - case SDLK_n: + case Keymap::TOGGLE_COLLISION: ToggleCollision(); break; - case SDLK_b: + case Keymap::PRINT_BLOCK: PrintBlockInfo(); break; - case SDLK_c: + case Keymap::PRINT_CHUNK: PrintChunkInfo(); break; - case SDLK_l: + case Keymap::PRINT_LIGHT: PrintLightInfo(); break; - case SDLK_p: + case Keymap::PRINT_SELECTION: PrintSelectionInfo(); break; - case SDLK_F3: - ToggleCounter(); + case Keymap::TOGGLE_VISUAL: + ToggleVisual(); + break; + case Keymap::TOGGLE_DEBUG: + ToggleDebug(); + break; + case Keymap::TOGGLE_AUDIO: + ToggleAudio(); + break; + + case Keymap::EXIT: + env.state.Pop(); + break; + + default: break; } } @@ -197,25 +235,28 @@ void Interface::HandlePress(const SDL_KeyboardEvent &event) { void Interface::HandleRelease(const SDL_KeyboardEvent &event) { if (config.keyboard_disabled) return; - switch (event.keysym.sym) { - case SDLK_w: + switch (env.keymap.Lookup(event)) { + case Keymap::MOVE_FORWARD: rev.z = 0; break; - case SDLK_s: + case Keymap::MOVE_BACKWARD: fwd.z = 0; break; - case SDLK_a: + case Keymap::MOVE_LEFT: rev.x = 0; break; - case SDLK_d: + case Keymap::MOVE_RIGHT: fwd.x = 0; break; - case SDLK_SPACE: + case Keymap::MOVE_UP: fwd.y = 0; break; - case SDLK_LSHIFT: + case Keymap::MOVE_DOWN: rev.y = 0; break; + + default: + break; } } @@ -231,61 +272,83 @@ void Interface::TurnBlock() { void Interface::ToggleCollision() { ctrl.Controlled().WorldCollidable(!ctrl.Controlled().WorldCollidable()); - std::cout << "collision " << (ctrl.Controlled().WorldCollidable() ? "on" : "off") << std::endl; + if (ctrl.Controlled().WorldCollidable()) { + PostMessage("collision on"); + } else { + PostMessage("collision off"); + } } void Interface::PrintBlockInfo() { std::cout << std::endl; - if (!aim_chunk) { - std::cout << "not looking at any block" << std::endl; + if (!aim_world) { + PostMessage("not looking at any block"); Ray aim = ctrl.Aim(); - std::cout << "aim ray: " << aim.orig << ", " << aim.dir << std::endl; + std::stringstream s; + s << "aim ray: " << aim.orig << ", " << aim.dir; + PostMessage(s.str()); return; } - std::cout << "looking at block " << aim_block - << " " << Chunk::ToCoords(aim_block) - << " of chunk " << aim_chunk->Position() - << std::endl; - Print(aim_chunk->BlockAt(aim_block)); + std::stringstream s; + s << "looking at block " << aim_world.block + << " " << aim_world.BlockCoords() + << " of chunk " << aim_world.GetChunk().Position() + ; + PostMessage(s.str()); + Print(aim_world.GetBlock()); } void Interface::PrintChunkInfo() { std::cout << std::endl; - if (!aim_chunk) { - std::cout << "not looking at any block" << std::endl; + if (!aim_world) { + PostMessage("not looking at any block"); return; } - std::cout << "looking at chunk " - << aim_chunk->Position() - << std::endl; - - std::cout << " neighbors:" << std::endl; - if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) { - std::cout << " left " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position() << std::endl; + std::stringstream s; + s << "looking at chunk " << aim_world.GetChunk().Position(); + PostMessage(s.str()); + + PostMessage(" neighbors:"); + if (aim_world.GetChunk().HasNeighbor(Block::FACE_LEFT)) { + s.str(""); + s << " left " << aim_world.GetChunk().GetNeighbor(Block::FACE_LEFT).Position(); + PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) { - std::cout << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position() << std::endl; + if (aim_world.GetChunk().HasNeighbor(Block::FACE_RIGHT)) { + s.str(""); + s << " right " << aim_world.GetChunk().GetNeighbor(Block::FACE_RIGHT).Position(); + PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_UP)) { - std::cout << " up " << aim_chunk->GetNeighbor(Block::FACE_UP).Position() << std::endl; + if (aim_world.GetChunk().HasNeighbor(Block::FACE_UP)) { + s.str(""); + s << " up " << aim_world.GetChunk().GetNeighbor(Block::FACE_UP).Position(); + PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) { - std::cout << " down " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position() << std::endl; + if (aim_world.GetChunk().HasNeighbor(Block::FACE_DOWN)) { + s.str(""); + s << " down " << aim_world.GetChunk().GetNeighbor(Block::FACE_DOWN).Position(); + PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) { - std::cout << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position() << std::endl; + if (aim_world.GetChunk().HasNeighbor(Block::FACE_FRONT)) { + s.str(""); + s << " front " << aim_world.GetChunk().GetNeighbor(Block::FACE_FRONT).Position(); + PostMessage(s.str()); } - if (aim_chunk->HasNeighbor(Block::FACE_BACK)) { - std::cout << " back " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position() << std::endl; + if (aim_world.GetChunk().HasNeighbor(Block::FACE_BACK)) { + s.str(""); + s << " back " << aim_world.GetChunk().GetNeighbor(Block::FACE_BACK).Position(); + PostMessage(s.str()); } std::cout << std::endl; } void Interface::PrintLightInfo() { - std::cout + std::stringstream s; + s << "light level " << world.PlayerChunk().GetLight(world.Player().Position()) << " at position " << world.Player().Position() - << std::endl; + ; + PostMessage(s.str()); } void Interface::PrintSelectionInfo() { @@ -294,30 +357,96 @@ void Interface::PrintSelectionInfo() { } void Interface::Print(const Block &block) { - std::cout << "type: " << block.type + std::stringstream s; + s << "type: " << block.type << ", face: " << block.GetFace() << ", turn: " << block.GetTurn() - << std::endl; + ; + PostMessage(s.str()); +} + +void Interface::ToggleAudio() { + config.audio_disabled = !config.audio_disabled; + if (config.audio_disabled) { + PostMessage("audio off"); + } else { + PostMessage("audio on"); + } +} + +void Interface::ToggleVisual() { + config.visual_disabled = !config.visual_disabled; + if (config.visual_disabled) { + PostMessage("visual off"); + } else { + PostMessage("visual on"); + } } -void Interface::ToggleCounter() { - if ((show_counter = !show_counter)) { +void Interface::ToggleDebug() { + debug = !debug; + if (debug) { UpdateCounter(); + UpdatePosition(); + UpdateOrientation(); + UpdateBlockInfo(); + UpdateEntityInfo(); } } void Interface::UpdateCounter() { std::stringstream s; - s << std::setprecision(3) << counter.AvgRunning() << "ms"; + s << std::setprecision(3) << + "avg: " << env.counter.Average().running << "ms, " + "peak: " << env.counter.Peak().running << "ms"; std::string text = s.str(); - font.Render(text.c_str(), counter_color, counter_tex); - glm::vec2 size(font.TextSize(text.c_str())); - counter_sprite.LoadRect(size.x, size.y); - counter_transform = glm::translate(glm::vec3( - 400.0f - size.x, - 25.0f, - 0.75f - )); + counter_text.Set(env.assets.small_ui_font, text); +} + +void Interface::UpdatePosition() { + std::stringstream s; + s << std::setprecision(3) << "pos: " << ctrl.Controlled().AbsolutePosition(); + position_text.Set(env.assets.small_ui_font, s.str()); +} + +void Interface::UpdateOrientation() { + std::stringstream s; + s << std::setprecision(3) << "pitch: " << rad2deg(ctrl.Pitch()) + << ", yaw: " << rad2deg(ctrl.Yaw()); + orientation_text.Set(env.assets.small_ui_font, s.str()); +} + +void Interface::UpdateBlockInfo() { + if (aim_world) { + const Block &block = aim_world.GetBlock(); + if (last_block != block) { + std::stringstream s; + s << "Block: " + << aim_world.GetType().label + << ", face: " << block.GetFace() + << ", turn: " << block.GetTurn(); + block_text.Set(env.assets.small_ui_font, s.str()); + last_block = block; + } + } else { + if (last_block != Block()) { + std::stringstream s; + s << "Block: none"; + block_text.Set(env.assets.small_ui_font, s.str()); + last_block = Block(); + } + } +} + +void Interface::UpdateEntityInfo() { + if (aim_entity) { + if (last_entity != aim_entity.entity) { + std::stringstream s; + s << "Entity: " << aim_entity.entity->Name(); + entity_text.Set(env.assets.small_ui_font, s.str()); + last_entity = aim_entity.entity; + } + } } @@ -352,27 +481,38 @@ void Interface::HandleRelease(const SDL_MouseButtonEvent &event) { } void Interface::PickBlock() { - if (!aim_chunk) return; - selection = aim_chunk->BlockAt(aim_block); + if (!aim_world) return; + selection = aim_world.GetBlock(); hud.Display(selection); } void Interface::PlaceBlock() { - if (!aim_chunk) return; - Chunk *mod_chunk = aim_chunk; - glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal; - if (!Chunk::InBounds(next_pos)) { - mod_chunk = &world.Next(*aim_chunk, aim_normal); - next_pos -= aim_normal * glm::vec3(Chunk::Extent()); + if (!aim_world) return; + + glm::vec3 next_pos = aim_world.BlockCoords() + aim_world.normal; + BlockLookup next_block(&aim_world.GetChunk(), next_pos); + if (next_block) { } - mod_chunk->SetBlock(next_pos, selection); - mod_chunk->Invalidate(); + next_block.SetBlock(selection); + + if (config.audio_disabled) return; + const Entity &player = ctrl.Controlled(); + env.audio.Play( + place_sound, + aim_world.GetChunk().ToSceneCoords(player.ChunkCoords(), next_pos) + ); } void Interface::RemoveBlock() noexcept { - if (!aim_chunk) return; - aim_chunk->SetBlock(aim_block, remove); - aim_chunk->Invalidate(); + if (!aim_world) return; + aim_world.SetBlock(remove); + + if (config.audio_disabled) return; + const Entity &player = ctrl.Controlled(); + env.audio.Play( + remove_sound, + aim_world.GetChunk().ToSceneCoords(player.ChunkCoords(), aim_world.BlockCoords()) + ); } @@ -402,10 +542,12 @@ void Interface::SelectPrevious() { hud.Display(selection); } -void Interface::Handle(const SDL_WindowEvent &event) noexcept { - if (event.event == SDL_WINDOWEVENT_RESIZED) { - hud.Viewport(event.data1, event.data2); - } + +void Interface::PostMessage(const char *msg) { + messages.PushLine(msg); + msg_timer.Reset(); + msg_timer.Start(); + std::cout << msg << std::endl; } @@ -413,12 +555,17 @@ void Interface::Update(int dt) { ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity); ctrl.Update(dt); + msg_timer.Update(dt); place_timer.Update(dt); remove_timer.Update(dt); aim = ctrl.Aim(); CheckAim(); + if (msg_timer.HitOnce()) { + msg_timer.Stop(); + } + if (remove_timer.Hit()) { RemoveBlock(); CheckAim(); @@ -429,42 +576,293 @@ void Interface::Update(int dt) { CheckAim(); } - if (show_counter && counter.Changed()) { - UpdateCounter(); + if (debug) { + if (env.counter.Changed()) { + UpdateCounter(); + } + UpdatePosition(); + UpdateOrientation(); } } +namespace { + +OutlineModel::Buffer outl_buf; + +} + void Interface::CheckAim() { - float dist; - if (world.Intersection(aim, glm::mat4(1.0f), aim_chunk, aim_block, dist, aim_normal)) { - outline.Clear(); - aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outline); - outline_transform = glm::scale(glm::vec3(1.0002f)); - outline_transform *= aim_chunk->Transform(world.Player().ChunkCoords()); - outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block); - } else { - aim_chunk = nullptr; + if (!world.Intersection(aim, glm::mat4(1.0f), ctrl.Controlled().ChunkCoords(), aim_world)) { + aim_world = WorldCollision(); + } + if (!world.Intersection(aim, glm::mat4(1.0f), ctrl.Controlled(), aim_entity)) { + aim_entity = EntityCollision(); + } + if (aim_world && aim_entity) { + // got both, pick the closest one + if (aim_world.depth < aim_entity.depth) { + UpdateOutline(); + aim_entity = EntityCollision(); + } else { + aim_world = WorldCollision(); + } + } else if (aim_world) { + UpdateOutline(); } + if (debug) { + UpdateBlockInfo(); + UpdateEntityInfo(); + } +} + +void Interface::UpdateOutline() { + outl_buf.Clear(); + aim_world.GetType().FillOutlineModel(outl_buf); + outline.Update(outl_buf); + outline_transform = aim_world.GetChunk().Transform(world.Player().ChunkCoords()); + outline_transform *= aim_world.BlockTransform(); + outline_transform *= glm::scale(glm::vec3(1.005f)); } -void Interface::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept { +void Interface::Render(Viewport &viewport) noexcept { if (config.visual_disabled) return; - if (aim_chunk) { - world_prog.Activate(); - world_prog.SetM(outline_transform); + if (aim_world) { + PlainColor &outline_prog = viewport.WorldOutlineProgram(); + outline_prog.SetM(outline_transform); outline.Draw(); } - if (show_counter) { - sprite_prog.Activate(); - sprite_prog.SetM(counter_transform); - sprite_prog.SetTexture(counter_tex); - counter_sprite.Draw(); + if (debug) { + counter_text.Render(viewport); + position_text.Render(viewport); + orientation_text.Render(viewport); + if (aim_world) { + block_text.Render(viewport); + } else if (aim_entity) { + entity_text.Render(viewport); + } + } + + if (msg_timer.Running()) { + messages.Render(viewport); + } + + hud.Render(viewport); +} + + +Keymap::Keymap() +: codemap{ NONE } { + +} + +void Keymap::Map(SDL_Scancode scancode, Action action) { + if (scancode > MAX_SCANCODE) { + throw std::runtime_error("refusing to map scancode: too damn high"); + } + codemap[scancode] = action; +} + +Keymap::Action Keymap::Lookup(SDL_Scancode scancode) { + if (scancode < NUM_SCANCODES) { + return codemap[scancode]; + } else { + return NONE; } +} + - hud.Render(world_prog, sprite_prog); +void Keymap::LoadDefault() { + Map(SDL_SCANCODE_UP, MOVE_FORWARD); + Map(SDL_SCANCODE_W, MOVE_FORWARD); + Map(SDL_SCANCODE_DOWN, MOVE_BACKWARD); + Map(SDL_SCANCODE_S, MOVE_BACKWARD); + Map(SDL_SCANCODE_LEFT, MOVE_LEFT); + Map(SDL_SCANCODE_A, MOVE_LEFT); + Map(SDL_SCANCODE_RIGHT, MOVE_RIGHT); + Map(SDL_SCANCODE_D, MOVE_RIGHT); + Map(SDL_SCANCODE_SPACE, MOVE_UP); + Map(SDL_SCANCODE_RSHIFT, MOVE_UP); + Map(SDL_SCANCODE_LSHIFT, MOVE_DOWN); + Map(SDL_SCANCODE_LCTRL, MOVE_DOWN); + Map(SDL_SCANCODE_RCTRL, MOVE_DOWN); + + Map(SDL_SCANCODE_Q, BLOCK_FACE); + Map(SDL_SCANCODE_E, BLOCK_TURN); + Map(SDL_SCANCODE_TAB, BLOCK_NEXT); + Map(SDL_SCANCODE_RIGHTBRACKET, BLOCK_NEXT); + Map(SDL_SCANCODE_LEFTBRACKET, BLOCK_PREV); + + Map(SDL_SCANCODE_INSERT, BLOCK_PLACE); + Map(SDL_SCANCODE_RETURN, BLOCK_PLACE); + Map(SDL_SCANCODE_MENU, BLOCK_PICK); + Map(SDL_SCANCODE_DELETE, BLOCK_REMOVE); + Map(SDL_SCANCODE_BACKSPACE, BLOCK_REMOVE); + + Map(SDL_SCANCODE_N, TOGGLE_COLLISION); + Map(SDL_SCANCODE_F1, TOGGLE_VISUAL); + Map(SDL_SCANCODE_F3, TOGGLE_DEBUG); + Map(SDL_SCANCODE_F4, TOGGLE_AUDIO); + + Map(SDL_SCANCODE_B, PRINT_BLOCK); + Map(SDL_SCANCODE_C, PRINT_CHUNK); + Map(SDL_SCANCODE_L, PRINT_LIGHT); + Map(SDL_SCANCODE_P, PRINT_SELECTION); + + Map(SDL_SCANCODE_ESCAPE, EXIT); +} + + +void Keymap::Load(std::istream &is) { + TokenStreamReader in(is); + std::string key_name; + std::string action_name; + SDL_Scancode key; + Action action; + while (in.HasMore()) { + if (in.Peek().type == Token::STRING) { + in.ReadString(key_name); + key = SDL_GetScancodeFromName(key_name.c_str()); + } else { + key = SDL_Scancode(in.GetInt()); + } + in.Skip(Token::EQUALS); + in.ReadIdentifier(action_name); + action = StringToAction(action_name); + if (in.HasMore() && in.Peek().type == Token::SEMICOLON) { + in.Skip(Token::SEMICOLON); + } + Map(key, action); + } +} + +void Keymap::Save(std::ostream &out) { + for (unsigned int i = 0; i < NUM_SCANCODES; ++i) { + if (codemap[i] == NONE) continue; + + const char *str = SDL_GetScancodeName(SDL_Scancode(i)); + if (str && *str) { + out << '"'; + while (*str) { + if (*str == '"') { + out << "\\\""; + } else { + out << *str; + } + ++str; + } + out << '"'; + } else { + out << i; + } + + out << " = " << ActionToString(codemap[i]) << std::endl;; + } +} + + +const char *Keymap::ActionToString(Action action) { + switch (action) { + default: + case NONE: + return "none"; + case MOVE_FORWARD: + return "move_forward"; + case MOVE_BACKWARD: + return "move_backward"; + case MOVE_LEFT: + return "move_left"; + case MOVE_RIGHT: + return "move_right"; + case MOVE_UP: + return "move_up"; + case MOVE_DOWN: + return "move_down"; + case BLOCK_FACE: + return "block_face"; + case BLOCK_TURN: + return "block_turn"; + case BLOCK_NEXT: + return "block_next"; + case BLOCK_PREV: + return "block_prev"; + case BLOCK_PLACE: + return "block_place"; + case BLOCK_PICK: + return "block_pick"; + case BLOCK_REMOVE: + return "block_remove"; + case TOGGLE_COLLISION: + return "toggle_collision"; + case TOGGLE_AUDIO: + return "toggle_audio"; + case TOGGLE_VISUAL: + return "toggle_visual"; + case TOGGLE_DEBUG: + return "toggle_debug"; + case PRINT_BLOCK: + return "print_block"; + case PRINT_CHUNK: + return "print_chunk"; + case PRINT_LIGHT: + return "print_light"; + case PRINT_SELECTION: + return "print_selection"; + case EXIT: + return "exit"; + } +} + +Keymap::Action Keymap::StringToAction(const std::string &str) { + if (str == "move_forward") { + return MOVE_FORWARD; + } else if (str == "move_backward") { + return MOVE_BACKWARD; + } else if (str == "move_left") { + return MOVE_LEFT; + } else if (str == "move_right") { + return MOVE_RIGHT; + } else if (str == "move_up") { + return MOVE_UP; + } else if (str == "move_down") { + return MOVE_DOWN; + } else if (str == "block_face") { + return BLOCK_FACE; + } else if (str == "block_turn") { + return BLOCK_TURN; + } else if (str == "block_next") { + return BLOCK_NEXT; + } else if (str == "block_prev") { + return BLOCK_PREV; + } else if (str == "block_place") { + return BLOCK_PLACE; + } else if (str == "block_pick") { + return BLOCK_PICK; + } else if (str == "block_remove") { + return BLOCK_REMOVE; + } else if (str == "toggle_collision") { + return TOGGLE_COLLISION; + } else if (str == "toggle_audio") { + return TOGGLE_AUDIO; + } else if (str == "toggle_visual") { + return TOGGLE_VISUAL; + } else if (str == "toggle_debug") { + return TOGGLE_DEBUG; + } else if (str == "print_block") { + return PRINT_BLOCK; + } else if (str == "print_chunk") { + return PRINT_CHUNK; + } else if (str == "print_light") { + return PRINT_LIGHT; + } else if (str == "print_selection") { + return PRINT_SELECTION; + } else if (str == "exit") { + return EXIT; + } else { + return NONE; + } } }