]> git.localhorst.tv Git - blank.git/blob - src/ui/ui.cpp
make buttons configurable
[blank.git] / src / ui / ui.cpp
1 #include "HUD.hpp"
2 #include "Interface.hpp"
3 #include "Keymap.hpp"
4
5 #include "../app/Assets.hpp"
6 #include "../app/Environment.hpp"
7 #include "../app/FrameCounter.hpp"
8 #include "../app/init.hpp"
9 #include "../audio/Audio.hpp"
10 #include "../graphics/Font.hpp"
11 #include "../graphics/Viewport.hpp"
12 #include "../io/TokenStreamReader.hpp"
13 #include "../model/shapes.hpp"
14 #include "../world/World.hpp"
15
16 #include <algorithm>
17 #include <cmath>
18 #include <iostream>
19 #include <sstream>
20 #include <glm/gtc/matrix_transform.hpp>
21 #include <glm/gtx/io.hpp>
22
23
24 namespace blank {
25
26 HUD::HUD(const BlockTypeRegistry &types, const Font &font)
27 : types(types)
28 , font(font)
29 , block()
30 , block_buf()
31 , block_transform(1.0f)
32 , block_label()
33 , block_visible(false)
34 , crosshair() {
35         block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f));
36         block_transform = glm::scale(block_transform, glm::vec3(50.0f));
37         block_transform = glm::rotate(block_transform, 3.5f, glm::vec3(1.0f, 0.0f, 0.0f));
38         block_transform = glm::rotate(block_transform, 0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
39
40         OutlineModel::Buffer buf;
41         buf.vertices = std::vector<glm::vec3>({
42                 { -10.0f,   0.0f, 0.0f }, { 10.0f,  0.0f, 0.0f },
43                 {   0.0f, -10.0f, 0.0f }, {  0.0f, 10.0f, 0.0f },
44         });
45         buf.indices = std::vector<OutlineModel::Index>({
46                 0, 1, 2, 3
47         });
48         buf.colors.resize(4, { 10.0f, 10.0f, 10.0f });
49         crosshair.Update(buf);
50
51         block_label.Position(
52                 glm::vec3(50.0f, 85.0f, 0.0f),
53                 Gravity::NORTH_WEST,
54                 Gravity::NORTH
55         );
56         block_label.Foreground(glm::vec4(1.0f));
57         block_label.Background(glm::vec4(0.5f));
58 }
59
60
61 void HUD::Display(const Block &b) {
62         const BlockType &type = types.Get(b.type);
63
64         block_buf.Clear();
65         type.FillEntityModel(block_buf, b.Transform());
66         block.Update(block_buf);
67
68         block_label.Set(font, type.label);
69
70         block_visible = type.visible;
71 }
72
73
74 void HUD::Render(Viewport &viewport) noexcept {
75         viewport.ClearDepth();
76
77         PlainColor &outline_prog = viewport.HUDOutlineProgram();
78         viewport.EnableInvertBlending();
79         viewport.SetCursor(glm::vec3(0.0f), Gravity::CENTER);
80         outline_prog.SetM(viewport.Cursor());
81         crosshair.Draw();
82
83         if (block_visible) {
84                 DirectionalLighting &world_prog = viewport.HUDProgram();
85                 world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f });
86                 // disable distance fog
87                 world_prog.SetFogDensity(0.0f);
88
89                 viewport.DisableBlending();
90                 world_prog.SetM(block_transform);
91                 block.Draw();
92                 block_label.Render(viewport);
93         }
94 }
95
96
97 Interface::Interface(
98         const Config &config,
99         Environment &env,
100         World &world)
101 : env(env)
102 , world(world)
103 , ctrl(world.Player())
104 , hud(world.BlockTypes(), env.assets.small_ui_font)
105 , aim{{ 0, 0, 0 }, { 0, 0, -1 }}
106 , aim_chunk(nullptr)
107 , aim_block(0)
108 , aim_normal()
109 , outline()
110 , outline_transform(1.0f)
111 , counter_text()
112 , position_text()
113 , orientation_text()
114 , messages(env.assets.small_ui_font)
115 , msg_timer(5000)
116 , config(config)
117 , place_timer(256)
118 , remove_timer(256)
119 , remove(0)
120 , selection(1)
121 , place_sound(env.assets.LoadSound("thump"))
122 , remove_sound(env.assets.LoadSound("plop"))
123 , fwd(0)
124 , rev(0)
125 , debug(false) {
126         counter_text.Position(glm::vec3(-25.0f, 25.0f, 0.0f), Gravity::NORTH_EAST);
127         counter_text.Foreground(glm::vec4(1.0f));
128         counter_text.Background(glm::vec4(0.5f));
129         position_text.Position(glm::vec3(-25.0f, 25.0f + env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST);
130         position_text.Foreground(glm::vec4(1.0f));
131         position_text.Background(glm::vec4(0.5f));
132         orientation_text.Position(glm::vec3(-25.0f, 25.0f + 2 * env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST);
133         orientation_text.Foreground(glm::vec4(1.0f));
134         orientation_text.Background(glm::vec4(0.5f));
135         messages.Position(glm::vec3(25.0f, -25.0f, 0.0f), Gravity::SOUTH_WEST);
136         messages.Foreground(glm::vec4(1.0f));
137         messages.Background(glm::vec4(0.5f));
138         hud.Display(selection);
139 }
140
141
142 void Interface::HandlePress(const SDL_KeyboardEvent &event) {
143         if (config.keyboard_disabled) return;
144
145         switch (env.keymap.Lookup(event)) {
146                 case Keymap::MOVE_FORWARD:
147                         rev.z = 1;
148                         break;
149                 case Keymap::MOVE_BACKWARD:
150                         fwd.z = 1;
151                         break;
152                 case Keymap::MOVE_LEFT:
153                         rev.x = 1;
154                         break;
155                 case Keymap::MOVE_RIGHT:
156                         fwd.x = 1;
157                         break;
158                 case Keymap::MOVE_UP:
159                         fwd.y = 1;
160                         break;
161                 case Keymap::MOVE_DOWN:
162                         rev.y = 1;
163                         break;
164
165                 case Keymap::BLOCK_FACE:
166                         FaceBlock();
167                         break;
168                 case Keymap::BLOCK_TURN:
169                         TurnBlock();
170                         break;
171                 case Keymap::BLOCK_NEXT:
172                         SelectNext();
173                         break;
174                 case Keymap::BLOCK_PREV:
175                         SelectPrevious();
176                         break;
177
178                 case Keymap::BLOCK_PLACE:
179                         PlaceBlock();
180                         break;
181                 case Keymap::BLOCK_PICK:
182                         PickBlock();
183                         break;
184                 case Keymap::BLOCK_REMOVE:
185                         RemoveBlock();
186                         break;
187
188                 case Keymap::TOGGLE_COLLISION:
189                         ToggleCollision();
190                         break;
191
192                 case Keymap::PRINT_BLOCK:
193                         PrintBlockInfo();
194                         break;
195                 case Keymap::PRINT_CHUNK:
196                         PrintChunkInfo();
197                         break;
198                 case Keymap::PRINT_LIGHT:
199                         PrintLightInfo();
200                         break;
201                 case Keymap::PRINT_SELECTION:
202                         PrintSelectionInfo();
203                         break;
204
205                 case Keymap::TOGGLE_VISUAL:
206                         ToggleVisual();
207                         break;
208                 case Keymap::TOGGLE_DEBUG:
209                         ToggleDebug();
210                         break;
211                 case Keymap::TOGGLE_AUDIO:
212                         ToggleAudio();
213                         break;
214
215                 case Keymap::EXIT:
216                         env.state.Pop();
217                         break;
218
219                 default:
220                         break;
221         }
222 }
223
224 void Interface::HandleRelease(const SDL_KeyboardEvent &event) {
225         if (config.keyboard_disabled) return;
226
227         switch (env.keymap.Lookup(event)) {
228                 case Keymap::MOVE_FORWARD:
229                         rev.z = 0;
230                         break;
231                 case Keymap::MOVE_BACKWARD:
232                         fwd.z = 0;
233                         break;
234                 case Keymap::MOVE_LEFT:
235                         rev.x = 0;
236                         break;
237                 case Keymap::MOVE_RIGHT:
238                         fwd.x = 0;
239                         break;
240                 case Keymap::MOVE_UP:
241                         fwd.y = 0;
242                         break;
243                 case Keymap::MOVE_DOWN:
244                         rev.y = 0;
245                         break;
246
247                 default:
248                         break;
249         }
250 }
251
252 void Interface::FaceBlock() {
253         selection.SetFace(Block::Face((selection.GetFace() + 1) % Block::FACE_COUNT));
254         hud.Display(selection);
255 }
256
257 void Interface::TurnBlock() {
258         selection.SetTurn(Block::Turn((selection.GetTurn() + 1) % Block::TURN_COUNT));
259         hud.Display(selection);
260 }
261
262 void Interface::ToggleCollision() {
263         ctrl.Controlled().WorldCollidable(!ctrl.Controlled().WorldCollidable());
264         if (ctrl.Controlled().WorldCollidable()) {
265                 PostMessage("collision on");
266         } else {
267                 PostMessage("collision off");
268         }
269 }
270
271 void Interface::PrintBlockInfo() {
272         std::cout << std::endl;
273         if (!aim_chunk) {
274                 PostMessage("not looking at any block");
275                 Ray aim = ctrl.Aim();
276                 std::stringstream s;
277                 s << "aim ray: " << aim.orig << ", " << aim.dir;
278                 PostMessage(s.str());
279                 return;
280         }
281         std::stringstream s;
282         s << "looking at block " << aim_block
283                 << " " << Chunk::ToCoords(aim_block)
284                 << " of chunk " << aim_chunk->Position()
285         ;
286         PostMessage(s.str());
287         Print(aim_chunk->BlockAt(aim_block));
288 }
289
290 void Interface::PrintChunkInfo() {
291         std::cout << std::endl;
292         if (!aim_chunk) {
293                 PostMessage("not looking at any block");
294                 return;
295         }
296         std::stringstream s;
297         s << "looking at chunk " << aim_chunk->Position();
298         PostMessage(s.str());
299
300         PostMessage("  neighbors:");
301         if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) {
302                 s.str("");
303                 s << " left  " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position();
304                 PostMessage(s.str());
305         }
306         if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) {
307                 s.str("");
308                 s << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position();
309                 PostMessage(s.str());
310         }
311         if (aim_chunk->HasNeighbor(Block::FACE_UP)) {
312                 s.str("");
313                 s << " up    " << aim_chunk->GetNeighbor(Block::FACE_UP).Position();
314                 PostMessage(s.str());
315         }
316         if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) {
317                 s.str("");
318                 s << " down  " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position();
319                 PostMessage(s.str());
320         }
321         if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) {
322                 s.str("");
323                 s << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position();
324                 PostMessage(s.str());
325         }
326         if (aim_chunk->HasNeighbor(Block::FACE_BACK)) {
327                 s.str("");
328                 s << " back  " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position();
329                 PostMessage(s.str());
330         }
331         std::cout << std::endl;
332 }
333
334 void Interface::PrintLightInfo() {
335         std::stringstream s;
336         s
337                 << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
338                 << " at position " << world.Player().Position()
339         ;
340         PostMessage(s.str());
341 }
342
343 void Interface::PrintSelectionInfo() {
344         std::cout << std::endl;
345         Print(selection);
346 }
347
348 void Interface::Print(const Block &block) {
349         std::stringstream s;
350         s << "type: " << block.type
351                 << ", face: " << block.GetFace()
352                 << ", turn: " << block.GetTurn()
353         ;
354         PostMessage(s.str());
355 }
356
357 void Interface::ToggleAudio() {
358         config.audio_disabled = !config.audio_disabled;
359         if (config.audio_disabled) {
360                 PostMessage("audio off");
361         } else {
362                 PostMessage("audio on");
363         }
364 }
365
366 void Interface::ToggleVisual() {
367         config.visual_disabled = !config.visual_disabled;
368         if (config.visual_disabled) {
369                 PostMessage("visual off");
370         } else {
371                 PostMessage("visual on");
372         }
373 }
374
375 void Interface::ToggleDebug() {
376         debug = !debug;
377         if (debug) {
378                 UpdateCounter();
379                 UpdatePosition();
380                 UpdateOrientation();
381         }
382 }
383
384 void Interface::UpdateCounter() {
385         std::stringstream s;
386         s << std::setprecision(3) <<
387                 "avg: " << env.counter.Average().running << "ms, "
388                 "peak: " << env.counter.Peak().running << "ms";
389         std::string text = s.str();
390         counter_text.Set(env.assets.small_ui_font, text);
391 }
392
393 void Interface::UpdatePosition() {
394         std::stringstream s;
395         s << std::setprecision(3) << "pos: " << ctrl.Controlled().AbsolutePosition();
396         position_text.Set(env.assets.small_ui_font, s.str());
397 }
398
399 void Interface::UpdateOrientation() {
400         std::stringstream s;
401         s << std::setprecision(3) << "pitch: " << rad2deg(ctrl.Pitch())
402                 << ", yaw: " << rad2deg(ctrl.Yaw());
403         orientation_text.Set(env.assets.small_ui_font, s.str());
404 }
405
406
407 void Interface::Handle(const SDL_MouseMotionEvent &event) {
408         if (config.mouse_disabled) return;
409         ctrl.RotateYaw(event.xrel * config.yaw_sensitivity);
410         ctrl.RotatePitch(event.yrel * config.pitch_sensitivity);
411 }
412
413 void Interface::HandlePress(const SDL_MouseButtonEvent &event) {
414         if (config.mouse_disabled) return;
415
416         if (event.button == SDL_BUTTON_LEFT) {
417                 RemoveBlock();
418                 remove_timer.Start();
419         } else if (event.button == SDL_BUTTON_MIDDLE) {
420                 PickBlock();
421         } else if (event.button == SDL_BUTTON_RIGHT) {
422                 PlaceBlock();
423                 place_timer.Start();
424         }
425 }
426
427 void Interface::HandleRelease(const SDL_MouseButtonEvent &event) {
428         if (config.mouse_disabled) return;
429
430         if (event.button == SDL_BUTTON_LEFT) {
431                 remove_timer.Stop();
432         } else if (event.button == SDL_BUTTON_RIGHT) {
433                 place_timer.Stop();
434         }
435 }
436
437 void Interface::PickBlock() {
438         if (!aim_chunk) return;
439         selection = aim_chunk->BlockAt(aim_block);
440         hud.Display(selection);
441 }
442
443 void Interface::PlaceBlock() {
444         if (!aim_chunk) return;
445         Chunk *mod_chunk = aim_chunk;
446         glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal;
447         if (!Chunk::InBounds(next_pos)) {
448                 mod_chunk = &world.Next(*aim_chunk, aim_normal);
449                 next_pos -= aim_normal * glm::vec3(Chunk::Extent());
450         }
451         mod_chunk->SetBlock(next_pos, selection);
452
453         if (config.audio_disabled) return;
454         const Entity &player = ctrl.Controlled();
455         env.audio.Play(
456                 place_sound,
457                 mod_chunk->ToSceneCoords(player.ChunkCoords(), next_pos)
458         );
459 }
460
461 void Interface::RemoveBlock() noexcept {
462         if (!aim_chunk) return;
463         aim_chunk->SetBlock(aim_block, remove);
464
465         if (config.audio_disabled) return;
466         const Entity &player = ctrl.Controlled();
467         env.audio.Play(
468                 remove_sound,
469                 aim_chunk->ToSceneCoords(player.ChunkCoords(), Chunk::ToCoords(aim_block))
470         );
471 }
472
473
474 void Interface::Handle(const SDL_MouseWheelEvent &event) {
475         if (config.mouse_disabled) return;
476
477         if (event.y < 0) {
478                 SelectNext();
479         } else if (event.y > 0) {
480                 SelectPrevious();
481         }
482 }
483
484 void Interface::SelectNext() {
485         ++selection.type;
486         if (size_t(selection.type) >= world.BlockTypes().Size()) {
487                 selection.type = 1;
488         }
489         hud.Display(selection);
490 }
491
492 void Interface::SelectPrevious() {
493         --selection.type;
494         if (selection.type <= 0) {
495                 selection.type = world.BlockTypes().Size() - 1;
496         }
497         hud.Display(selection);
498 }
499
500
501 void Interface::PostMessage(const char *msg) {
502         messages.PushLine(msg);
503         msg_timer.Reset();
504         msg_timer.Start();
505         std::cout << msg << std::endl;
506 }
507
508
509 void Interface::Update(int dt) {
510         ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity);
511         ctrl.Update(dt);
512
513         msg_timer.Update(dt);
514         place_timer.Update(dt);
515         remove_timer.Update(dt);
516
517         aim = ctrl.Aim();
518         CheckAim();
519
520         if (msg_timer.HitOnce()) {
521                 msg_timer.Stop();
522         }
523
524         if (remove_timer.Hit()) {
525                 RemoveBlock();
526                 CheckAim();
527         }
528
529         if (place_timer.Hit()) {
530                 PlaceBlock();
531                 CheckAim();
532         }
533
534         if (debug) {
535                 if (env.counter.Changed()) {
536                         UpdateCounter();
537                 }
538                 UpdatePosition();
539                 UpdateOrientation();
540         }
541 }
542
543 namespace {
544
545 OutlineModel::Buffer outl_buf;
546
547 }
548
549 void Interface::CheckAim() {
550         float dist;
551         if (world.Intersection(aim, glm::mat4(1.0f), aim_chunk, aim_block, dist, aim_normal)) {
552                 outl_buf.Clear();
553                 aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outl_buf);
554                 outline.Update(outl_buf);
555                 outline_transform = aim_chunk->Transform(world.Player().ChunkCoords());
556                 outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block);
557                 outline_transform *= glm::scale(glm::vec3(1.005f));
558         } else {
559                 aim_chunk = nullptr;
560         }
561 }
562
563
564 void Interface::Render(Viewport &viewport) noexcept {
565         if (config.visual_disabled) return;
566
567         if (aim_chunk) {
568                 PlainColor &outline_prog = viewport.WorldOutlineProgram();
569                 outline_prog.SetM(outline_transform);
570                 outline.Draw();
571         }
572
573         if (debug) {
574                 counter_text.Render(viewport);
575                 position_text.Render(viewport);
576                 orientation_text.Render(viewport);
577         }
578
579         if (msg_timer.Running()) {
580                 messages.Render(viewport);
581         }
582
583         hud.Render(viewport);
584 }
585
586
587 Keymap::Keymap()
588 : codemap{ NONE } {
589
590 }
591
592 void Keymap::Map(SDL_Scancode scancode, Action action) {
593         if (scancode > MAX_SCANCODE) {
594                 throw std::runtime_error("refusing to map scancode: too damn high");
595         }
596         codemap[scancode] = action;
597 }
598
599 Keymap::Action Keymap::Lookup(SDL_Scancode scancode) {
600         if (scancode < NUM_SCANCODES) {
601                 return codemap[scancode];
602         } else {
603                 return NONE;
604         }
605 }
606
607
608 void Keymap::LoadDefault() {
609         Map(SDL_SCANCODE_UP, MOVE_FORWARD);
610         Map(SDL_SCANCODE_W, MOVE_FORWARD);
611         Map(SDL_SCANCODE_DOWN, MOVE_BACKWARD);
612         Map(SDL_SCANCODE_S, MOVE_BACKWARD);
613         Map(SDL_SCANCODE_LEFT, MOVE_LEFT);
614         Map(SDL_SCANCODE_A, MOVE_LEFT);
615         Map(SDL_SCANCODE_RIGHT, MOVE_RIGHT);
616         Map(SDL_SCANCODE_D, MOVE_RIGHT);
617         Map(SDL_SCANCODE_SPACE, MOVE_UP);
618         Map(SDL_SCANCODE_RSHIFT, MOVE_UP);
619         Map(SDL_SCANCODE_LSHIFT, MOVE_DOWN);
620         Map(SDL_SCANCODE_LCTRL, MOVE_DOWN);
621         Map(SDL_SCANCODE_RCTRL, MOVE_DOWN);
622
623         Map(SDL_SCANCODE_Q, BLOCK_FACE);
624         Map(SDL_SCANCODE_E, BLOCK_TURN);
625         Map(SDL_SCANCODE_TAB, BLOCK_NEXT);
626         Map(SDL_SCANCODE_RIGHTBRACKET, BLOCK_NEXT);
627         Map(SDL_SCANCODE_LEFTBRACKET, BLOCK_PREV);
628
629         Map(SDL_SCANCODE_INSERT, BLOCK_PLACE);
630         Map(SDL_SCANCODE_RETURN, BLOCK_PLACE);
631         Map(SDL_SCANCODE_MENU, BLOCK_PICK);
632         Map(SDL_SCANCODE_DELETE, BLOCK_REMOVE);
633         Map(SDL_SCANCODE_BACKSPACE, BLOCK_REMOVE);
634
635         Map(SDL_SCANCODE_N, TOGGLE_COLLISION);
636         Map(SDL_SCANCODE_F1, TOGGLE_VISUAL);
637         Map(SDL_SCANCODE_F3, TOGGLE_DEBUG);
638         Map(SDL_SCANCODE_F4, TOGGLE_AUDIO);
639
640         Map(SDL_SCANCODE_B, PRINT_BLOCK);
641         Map(SDL_SCANCODE_C, PRINT_CHUNK);
642         Map(SDL_SCANCODE_L, PRINT_LIGHT);
643         Map(SDL_SCANCODE_P, PRINT_SELECTION);
644
645         Map(SDL_SCANCODE_ESCAPE, EXIT);
646 }
647
648
649 void Keymap::Load(std::istream &is) {
650         TokenStreamReader in(is);
651         std::string key_name;
652         std::string action_name;
653         SDL_Scancode key;
654         Action action;
655         while (in.HasMore()) {
656                 if (in.Peek().type == Token::STRING) {
657                         in.ReadString(key_name);
658                         key = SDL_GetScancodeFromName(key_name.c_str());
659                 } else {
660                         key = SDL_Scancode(in.GetInt());
661                 }
662                 in.Skip(Token::EQUALS);
663                 in.ReadIdentifier(action_name);
664                 action = StringToAction(action_name);
665                 if (in.HasMore() && in.Peek().type == Token::SEMICOLON) {
666                         in.Skip(Token::SEMICOLON);
667                 }
668                 Map(key, action);
669         }
670 }
671
672 void Keymap::Save(std::ostream &out) {
673         for (unsigned int i = 0; i < NUM_SCANCODES; ++i) {
674                 if (codemap[i] == NONE) continue;
675
676                 const char *str = SDL_GetScancodeName(SDL_Scancode(i));
677                 if (str && *str) {
678                         out << '"';
679                         while (*str) {
680                                 if (*str == '"') {
681                                         out << "\\\"";
682                                 } else {
683                                         out << *str;
684                                 }
685                                 ++str;
686                         }
687                         out << '"';
688                 } else {
689                         out << i;
690                 }
691
692                 out << " = " << ActionToString(codemap[i]) << std::endl;;
693         }
694 }
695
696
697 const char *Keymap::ActionToString(Action action) {
698         switch (action) {
699                 default:
700                 case NONE:
701                         return "none";
702                 case MOVE_FORWARD:
703                         return "move_forward";
704                 case MOVE_BACKWARD:
705                         return "move_backward";
706                 case MOVE_LEFT:
707                         return "move_left";
708                 case MOVE_RIGHT:
709                         return "move_right";
710                 case MOVE_UP:
711                         return "move_up";
712                 case MOVE_DOWN:
713                         return "move_down";
714                 case BLOCK_FACE:
715                         return "block_face";
716                 case BLOCK_TURN:
717                         return "block_turn";
718                 case BLOCK_NEXT:
719                         return "block_next";
720                 case BLOCK_PREV:
721                         return "block_prev";
722                 case BLOCK_PLACE:
723                         return "block_place";
724                 case BLOCK_PICK:
725                         return "block_pick";
726                 case BLOCK_REMOVE:
727                         return "block_remove";
728                 case TOGGLE_COLLISION:
729                         return "toggle_collision";
730                 case TOGGLE_AUDIO:
731                         return "toggle_audio";
732                 case TOGGLE_VISUAL:
733                         return "toggle_visual";
734                 case TOGGLE_DEBUG:
735                         return "toggle_debug";
736                 case PRINT_BLOCK:
737                         return "print_block";
738                 case PRINT_CHUNK:
739                         return "print_chunk";
740                 case PRINT_LIGHT:
741                         return "print_light";
742                 case PRINT_SELECTION:
743                         return "print_selection";
744                 case EXIT:
745                         return "exit";
746         }
747 }
748
749 Keymap::Action Keymap::StringToAction(const std::string &str) {
750         if (str == "move_forward") {
751                 return MOVE_FORWARD;
752         } else if (str == "move_backward") {
753                 return MOVE_BACKWARD;
754         } else if (str == "move_left") {
755                 return MOVE_LEFT;
756         } else if (str == "move_right") {
757                 return MOVE_RIGHT;
758         } else if (str == "move_up") {
759                 return MOVE_UP;
760         } else if (str == "move_down") {
761                 return MOVE_DOWN;
762         } else if (str == "block_face") {
763                 return BLOCK_FACE;
764         } else if (str == "block_turn") {
765                 return BLOCK_TURN;
766         } else if (str == "block_next") {
767                 return BLOCK_NEXT;
768         } else if (str == "block_prev") {
769                 return BLOCK_PREV;
770         } else if (str == "block_place") {
771                 return BLOCK_PLACE;
772         } else if (str == "block_pick") {
773                 return BLOCK_PICK;
774         } else if (str == "block_remove") {
775                 return BLOCK_REMOVE;
776         } else if (str == "toggle_collision") {
777                 return TOGGLE_COLLISION;
778         } else if (str == "toggle_audio") {
779                 return TOGGLE_AUDIO;
780         } else if (str == "toggle_visual") {
781                 return TOGGLE_VISUAL;
782         } else if (str == "toggle_debug") {
783                 return TOGGLE_DEBUG;
784         } else if (str == "print_block") {
785                 return PRINT_BLOCK;
786         } else if (str == "print_chunk") {
787                 return PRINT_CHUNK;
788         } else if (str == "print_light") {
789                 return PRINT_LIGHT;
790         } else if (str == "print_selection") {
791                 return PRINT_SELECTION;
792         } else if (str == "exit") {
793                 return EXIT;
794         } else {
795                 return NONE;
796         }
797 }
798
799 }