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